From a0339d2458867dbe9485499265641ff205063445 Mon Sep 17 00:00:00 2001 From: lv-426 Date: Sun, 22 Jun 2008 18:20:35 +0000 Subject: [PATCH] initial GNU TLS import - this should reduce in size considerable --- configure.ac | 44 +- src/daemon/Makefile.am | 40 +- src/daemon/https/Makefile.am | 34 + src/daemon/https/errcodes | 131 + src/daemon/https/errcodes.c | 65 + src/daemon/https/https_common.c | 877 ++++ src/daemon/https/https_common.h | 40 + src/daemon/https/includes/Makefile.am | 3 + src/daemon/https/includes/compat.h | 98 + src/daemon/https/includes/extra.h | 185 + src/daemon/https/includes/gnutls.h | 1286 +++++ src/daemon/https/includes/gnutlsxx.h | 393 ++ src/daemon/https/lgl/Makefile.am | 37 + src/daemon/https/lgl/asnprintf.c | 35 + src/daemon/https/lgl/asprintf.c | 39 + src/daemon/https/lgl/des.c | 662 +++ src/daemon/https/lgl/des.h | 121 + src/daemon/https/lgl/float+.h | 148 + src/daemon/https/lgl/gc-gnulib.c | 791 +++ src/daemon/https/lgl/gc-libgcrypt.c | 627 +++ src/daemon/https/lgl/gc-pbkdf2-sha1.c | 185 + src/daemon/https/lgl/gc.h | 347 ++ src/daemon/https/lgl/gettext.h | 270 + src/daemon/https/lgl/hmac-md5.c | 81 + src/daemon/https/lgl/hmac-sha1.c | 81 + src/daemon/https/lgl/hmac.h | 41 + src/daemon/https/lgl/md5.c | 451 ++ src/daemon/https/lgl/md5.h | 124 + src/daemon/https/lgl/memmem.c | 61 + src/daemon/https/lgl/memmove.c | 26 + src/daemon/https/lgl/memxor.c | 35 + src/daemon/https/lgl/memxor.h | 31 + src/daemon/https/lgl/printf-args.c | 185 + src/daemon/https/lgl/printf-args.h | 154 + src/daemon/https/lgl/printf-parse.c | 599 +++ src/daemon/https/lgl/printf-parse.h | 178 + src/daemon/https/lgl/read-file.c | 136 + src/daemon/https/lgl/read-file.h | 34 + src/daemon/https/lgl/realloc.c | 87 + src/daemon/https/lgl/rijndael-alg-fst.c | 1083 ++++ src/daemon/https/lgl/rijndael-alg-fst.h | 67 + src/daemon/https/lgl/rijndael-api-fst.c | 517 ++ src/daemon/https/lgl/rijndael-api-fst.h | 207 + src/daemon/https/lgl/sha1.c | 416 ++ src/daemon/https/lgl/sha1.h | 87 + src/daemon/https/lgl/snprintf.c | 77 + src/daemon/https/lgl/strverscmp.c | 130 + src/daemon/https/lgl/strverscmp.h | 24 + src/daemon/https/lgl/time_r.c | 46 + src/daemon/https/lgl/vasnprintf.c | 4825 ++++++++++++++++++ src/daemon/https/lgl/vasnprintf.h | 81 + src/daemon/https/lgl/vasprintf.c | 56 + src/daemon/https/lgl/xsize.h | 109 + src/daemon/https/list.h | 449 ++ src/daemon/https/minitasn1/Makefile.am | 16 + src/daemon/https/minitasn1/README | 3 + src/daemon/https/minitasn1/coding.c | 1229 +++++ src/daemon/https/minitasn1/decoding.c | 2821 ++++++++++ src/daemon/https/minitasn1/element.c | 1013 ++++ src/daemon/https/minitasn1/element.h | 13 + src/daemon/https/minitasn1/errors.c | 135 + src/daemon/https/minitasn1/errors.h | 30 + src/daemon/https/minitasn1/gstr.c | 68 + src/daemon/https/minitasn1/gstr.h | 5 + src/daemon/https/minitasn1/int.h | 112 + src/daemon/https/minitasn1/libtasn1.h | 246 + src/daemon/https/minitasn1/mem.h | 27 + src/daemon/https/minitasn1/parser_aux.c | 1072 ++++ src/daemon/https/minitasn1/parser_aux.h | 63 + src/daemon/https/minitasn1/structure.c | 1221 +++++ src/daemon/https/minitasn1/structure.h | 23 + src/daemon/https/opencdk/Makefile.am | 12 + src/daemon/https/opencdk/README | 5 + src/daemon/https/opencdk/armor.c | 763 +++ src/daemon/https/opencdk/cipher.c | 528 ++ src/daemon/https/opencdk/compress.c | 238 + src/daemon/https/opencdk/context.h | 120 + src/daemon/https/opencdk/dummy.c | 15 + src/daemon/https/opencdk/filters.h | 95 + src/daemon/https/opencdk/kbnode.c | 581 +++ src/daemon/https/opencdk/keydb.c | 2303 +++++++++ src/daemon/https/opencdk/literal.c | 307 ++ src/daemon/https/opencdk/main.c | 779 +++ src/daemon/https/opencdk/main.h | 183 + src/daemon/https/opencdk/misc.c | 571 +++ src/daemon/https/opencdk/new-packet.c | 874 ++++ src/daemon/https/opencdk/opencdk.h | 1169 +++++ src/daemon/https/opencdk/packet.h | 40 + src/daemon/https/opencdk/pubkey.c | 1380 +++++ src/daemon/https/opencdk/read-packet.c | 1179 +++++ src/daemon/https/opencdk/seskey.c | 717 +++ src/daemon/https/opencdk/sig-check.c | 489 ++ src/daemon/https/opencdk/stream.c | 1446 ++++++ src/daemon/https/opencdk/stream.h | 88 + src/daemon/https/opencdk/types.h | 44 + src/daemon/https/opencdk/verify.c | 306 ++ src/daemon/https/opencdk/write-packet.c | 947 ++++ src/daemon/https/openpgp/Makefile.am | 27 + src/daemon/https/openpgp/compat.c | 252 + src/daemon/https/openpgp/extras.c | 165 + src/daemon/https/openpgp/gnutls_extra.c | 110 + src/daemon/https/openpgp/gnutls_extra.h | 50 + src/daemon/https/openpgp/gnutls_ia.c | 905 ++++ src/daemon/https/openpgp/gnutls_openpgp.c | 973 ++++ src/daemon/https/openpgp/gnutls_openpgp.h | 98 + src/daemon/https/openpgp/openpgp.h | 182 + src/daemon/https/openpgp/pgp.c | 534 ++ src/daemon/https/openpgp/pgp_privkey.c | 134 + src/daemon/https/openpgp/pgp_verify.c | 144 + src/daemon/https/tests.c | 1160 +++++ src/daemon/https/tests.h | 42 + src/daemon/https/tls/Makefile.am | 73 + src/daemon/https/tls/auth_anon.c | 178 + src/daemon/https/tls/auth_anon.h | 48 + src/daemon/https/tls/auth_cert.c | 1781 +++++++ src/daemon/https/tls/auth_cert.h | 158 + src/daemon/https/tls/auth_dh_common.c | 369 ++ src/daemon/https/tls/auth_dh_common.h | 48 + src/daemon/https/tls/auth_dhe.c | 276 + src/daemon/https/tls/auth_rsa.c | 408 ++ src/daemon/https/tls/auth_rsa_export.c | 325 ++ src/daemon/https/tls/debug.c | 128 + src/daemon/https/tls/debug.h | 30 + src/daemon/https/tls/defines.h | 64 + src/daemon/https/tls/ext_cert_type.c | 243 + src/daemon/https/tls/ext_cert_type.h | 31 + src/daemon/https/tls/ext_inner_application.c | 147 + src/daemon/https/tls/ext_inner_application.h | 29 + src/daemon/https/tls/ext_max_record.c | 196 + src/daemon/https/tls/ext_max_record.h | 33 + src/daemon/https/tls/ext_oprfi.c | 253 + src/daemon/https/tls/ext_oprfi.h | 33 + src/daemon/https/tls/ext_server_name.c | 330 ++ src/daemon/https/tls/ext_server_name.h | 28 + src/daemon/https/tls/gnutls.asn | 93 + src/daemon/https/tls/gnutls.pc | 23 + src/daemon/https/tls/gnutls_alert.c | 304 ++ src/daemon/https/tls/gnutls_algorithms.c | 2225 ++++++++ src/daemon/https/tls/gnutls_algorithms.h | 141 + src/daemon/https/tls/gnutls_anon_cred.c | 137 + src/daemon/https/tls/gnutls_asn1_tab.c | 63 + src/daemon/https/tls/gnutls_auth.c | 413 ++ src/daemon/https/tls/gnutls_auth.h | 50 + src/daemon/https/tls/gnutls_auth_int.h | 32 + src/daemon/https/tls/gnutls_buffer.h | 31 + src/daemon/https/tls/gnutls_buffers.c | 1241 +++++ src/daemon/https/tls/gnutls_buffers.h | 67 + src/daemon/https/tls/gnutls_cert.c | 918 ++++ src/daemon/https/tls/gnutls_cert.h | 132 + src/daemon/https/tls/gnutls_cipher.c | 584 +++ src/daemon/https/tls/gnutls_cipher.h | 41 + src/daemon/https/tls/gnutls_cipher_int.c | 133 + src/daemon/https/tls/gnutls_cipher_int.h | 46 + src/daemon/https/tls/gnutls_compress.c | 80 + src/daemon/https/tls/gnutls_compress.h | 30 + src/daemon/https/tls/gnutls_compress_int.c | 300 ++ src/daemon/https/tls/gnutls_compress_int.h | 49 + src/daemon/https/tls/gnutls_constate.c | 1039 ++++ src/daemon/https/tls/gnutls_constate.h | 40 + src/daemon/https/tls/gnutls_datum.c | 114 + src/daemon/https/tls/gnutls_datum.h | 40 + src/daemon/https/tls/gnutls_db.c | 386 ++ src/daemon/https/tls/gnutls_db.h | 37 + src/daemon/https/tls/gnutls_dh.c | 162 + src/daemon/https/tls/gnutls_dh.h | 38 + src/daemon/https/tls/gnutls_dh_primes.c | 626 +++ src/daemon/https/tls/gnutls_errors.c | 422 ++ src/daemon/https/tls/gnutls_errors.h | 73 + src/daemon/https/tls/gnutls_extensions.c | 318 ++ src/daemon/https/tls/gnutls_extensions.h | 45 + src/daemon/https/tls/gnutls_extra_hooks.c | 78 + src/daemon/https/tls/gnutls_extra_hooks.h | 106 + src/daemon/https/tls/gnutls_global.c | 375 ++ src/daemon/https/tls/gnutls_global.h | 42 + src/daemon/https/tls/gnutls_handshake.c | 3029 +++++++++++ src/daemon/https/tls/gnutls_handshake.h | 60 + src/daemon/https/tls/gnutls_hash_int.c | 446 ++ src/daemon/https/tls/gnutls_hash_int.h | 72 + src/daemon/https/tls/gnutls_int.h | 679 +++ src/daemon/https/tls/gnutls_kx.c | 773 +++ src/daemon/https/tls/gnutls_kx.h | 39 + src/daemon/https/tls/gnutls_mem.c | 134 + src/daemon/https/tls/gnutls_mem.h | 70 + src/daemon/https/tls/gnutls_mpi.c | 285 ++ src/daemon/https/tls/gnutls_mpi.h | 78 + src/daemon/https/tls/gnutls_num.c | 192 + src/daemon/https/tls/gnutls_num.h | 49 + src/daemon/https/tls/gnutls_pk.c | 915 ++++ src/daemon/https/tls/gnutls_pk.h | 46 + src/daemon/https/tls/gnutls_priority.c | 497 ++ src/daemon/https/tls/gnutls_record.c | 1204 +++++ src/daemon/https/tls/gnutls_record.h | 32 + src/daemon/https/tls/gnutls_rsa_export.c | 361 ++ src/daemon/https/tls/gnutls_rsa_export.h | 27 + src/daemon/https/tls/gnutls_session.c | 199 + src/daemon/https/tls/gnutls_session.h | 23 + src/daemon/https/tls/gnutls_session_pack.c | 1204 +++++ src/daemon/https/tls/gnutls_session_pack.h | 28 + src/daemon/https/tls/gnutls_sig.c | 473 ++ src/daemon/https/tls/gnutls_sig.h | 51 + src/daemon/https/tls/gnutls_state.c | 1219 +++++ src/daemon/https/tls/gnutls_state.h | 72 + src/daemon/https/tls/gnutls_str.c | 312 ++ src/daemon/https/tls/gnutls_str.h | 67 + src/daemon/https/tls/gnutls_supplemental.c | 207 + src/daemon/https/tls/gnutls_supplemental.h | 31 + src/daemon/https/tls/gnutls_ui.c | 627 +++ src/daemon/https/tls/gnutls_v2_compat.c | 259 + src/daemon/https/tls/gnutls_v2_compat.h | 26 + src/daemon/https/tls/gnutls_x509.c | 1991 ++++++++ src/daemon/https/tls/gnutls_x509.h | 49 + src/daemon/https/tls/gnutlsxx.cpp | 907 ++++ src/daemon/https/tls/io_debug.h | 79 + src/daemon/https/tls/libgnutls-config | 104 + src/daemon/https/tls/libgnutls.m4 | 160 + src/daemon/https/tls/libgnutls.vers | 27 + src/daemon/https/tls/libgnutlsxx.vers | 30 + src/daemon/https/tls/pkix.asn | 1241 +++++ src/daemon/https/tls/pkix_asn1_tab.c | 1130 ++++ src/daemon/https/tls/x509_b64.c | 599 +++ src/daemon/https/tls/x509_b64.h | 45 + src/daemon/https/tls_test.c | 348 ++ src/daemon/https/x509/Makefile.am | 36 + src/daemon/https/x509/common.c | 1491 ++++++ src/daemon/https/x509/common.h | 126 + src/daemon/https/x509/crl.c | 705 +++ src/daemon/https/x509/crl_write.c | 316 ++ src/daemon/https/x509/crq.c | 887 ++++ src/daemon/https/x509/crq.h | 30 + src/daemon/https/x509/dn.c | 1140 +++++ src/daemon/https/x509/dn.h | 58 + src/daemon/https/x509/dsa.c | 142 + src/daemon/https/x509/dsa.h | 25 + src/daemon/https/x509/extensions.c | 1095 ++++ src/daemon/https/x509/extensions.h | 68 + src/daemon/https/x509/mpi.c | 576 +++ src/daemon/https/x509/mpi.h | 57 + src/daemon/https/x509/output.c | 1360 +++++ src/daemon/https/x509/pkcs12.c | 1325 +++++ src/daemon/https/x509/pkcs12.h | 208 + src/daemon/https/x509/pkcs12_bag.c | 770 +++ src/daemon/https/x509/pkcs12_encr.c | 169 + src/daemon/https/x509/pkcs7.c | 1023 ++++ src/daemon/https/x509/pkcs7.h | 30 + src/daemon/https/x509/privkey.h | 31 + src/daemon/https/x509/privkey_pkcs8.c | 2219 ++++++++ src/daemon/https/x509/rfc2818.h | 26 + src/daemon/https/x509/rfc2818_hostname.c | 160 + src/daemon/https/x509/sign.c | 346 ++ src/daemon/https/x509/sign.h | 36 + src/daemon/https/x509/verify.h | 34 + src/daemon/https/x509/x509-api.texi | 2943 +++++++++++ src/daemon/https/x509/x509.c | 2851 +++++++++++ src/daemon/https/x509/x509.h | 928 ++++ src/daemon/https/x509/x509_privkey.c | 1509 ++++++ src/daemon/https/x509/x509_verify.c | 1037 ++++ src/daemon/https/x509/x509_write.c | 1094 ++++ 257 files changed, 106595 insertions(+), 23 deletions(-) create mode 100644 src/daemon/https/Makefile.am create mode 100644 src/daemon/https/errcodes create mode 100644 src/daemon/https/errcodes.c create mode 100644 src/daemon/https/https_common.c create mode 100644 src/daemon/https/https_common.h create mode 100644 src/daemon/https/includes/Makefile.am create mode 100644 src/daemon/https/includes/compat.h create mode 100644 src/daemon/https/includes/extra.h create mode 100644 src/daemon/https/includes/gnutls.h create mode 100644 src/daemon/https/includes/gnutlsxx.h create mode 100644 src/daemon/https/lgl/Makefile.am create mode 100644 src/daemon/https/lgl/asnprintf.c create mode 100644 src/daemon/https/lgl/asprintf.c create mode 100644 src/daemon/https/lgl/des.c create mode 100644 src/daemon/https/lgl/des.h create mode 100644 src/daemon/https/lgl/float+.h create mode 100644 src/daemon/https/lgl/gc-gnulib.c create mode 100644 src/daemon/https/lgl/gc-libgcrypt.c create mode 100644 src/daemon/https/lgl/gc-pbkdf2-sha1.c create mode 100644 src/daemon/https/lgl/gc.h create mode 100644 src/daemon/https/lgl/gettext.h create mode 100644 src/daemon/https/lgl/hmac-md5.c create mode 100644 src/daemon/https/lgl/hmac-sha1.c create mode 100644 src/daemon/https/lgl/hmac.h create mode 100644 src/daemon/https/lgl/md5.c create mode 100644 src/daemon/https/lgl/md5.h create mode 100644 src/daemon/https/lgl/memmem.c create mode 100644 src/daemon/https/lgl/memmove.c create mode 100644 src/daemon/https/lgl/memxor.c create mode 100644 src/daemon/https/lgl/memxor.h create mode 100644 src/daemon/https/lgl/printf-args.c create mode 100644 src/daemon/https/lgl/printf-args.h create mode 100644 src/daemon/https/lgl/printf-parse.c create mode 100644 src/daemon/https/lgl/printf-parse.h create mode 100644 src/daemon/https/lgl/read-file.c create mode 100644 src/daemon/https/lgl/read-file.h create mode 100644 src/daemon/https/lgl/realloc.c create mode 100644 src/daemon/https/lgl/rijndael-alg-fst.c create mode 100644 src/daemon/https/lgl/rijndael-alg-fst.h create mode 100644 src/daemon/https/lgl/rijndael-api-fst.c create mode 100644 src/daemon/https/lgl/rijndael-api-fst.h create mode 100644 src/daemon/https/lgl/sha1.c create mode 100644 src/daemon/https/lgl/sha1.h create mode 100644 src/daemon/https/lgl/snprintf.c create mode 100644 src/daemon/https/lgl/strverscmp.c create mode 100644 src/daemon/https/lgl/strverscmp.h create mode 100644 src/daemon/https/lgl/time_r.c create mode 100644 src/daemon/https/lgl/vasnprintf.c create mode 100644 src/daemon/https/lgl/vasnprintf.h create mode 100644 src/daemon/https/lgl/vasprintf.c create mode 100644 src/daemon/https/lgl/xsize.h create mode 100644 src/daemon/https/list.h create mode 100644 src/daemon/https/minitasn1/Makefile.am create mode 100644 src/daemon/https/minitasn1/README create mode 100644 src/daemon/https/minitasn1/coding.c create mode 100644 src/daemon/https/minitasn1/decoding.c create mode 100644 src/daemon/https/minitasn1/element.c create mode 100644 src/daemon/https/minitasn1/element.h create mode 100644 src/daemon/https/minitasn1/errors.c create mode 100644 src/daemon/https/minitasn1/errors.h create mode 100644 src/daemon/https/minitasn1/gstr.c create mode 100644 src/daemon/https/minitasn1/gstr.h create mode 100644 src/daemon/https/minitasn1/int.h create mode 100644 src/daemon/https/minitasn1/libtasn1.h create mode 100644 src/daemon/https/minitasn1/mem.h create mode 100644 src/daemon/https/minitasn1/parser_aux.c create mode 100644 src/daemon/https/minitasn1/parser_aux.h create mode 100644 src/daemon/https/minitasn1/structure.c create mode 100644 src/daemon/https/minitasn1/structure.h create mode 100644 src/daemon/https/opencdk/Makefile.am create mode 100644 src/daemon/https/opencdk/README create mode 100644 src/daemon/https/opencdk/armor.c create mode 100644 src/daemon/https/opencdk/cipher.c create mode 100644 src/daemon/https/opencdk/compress.c create mode 100644 src/daemon/https/opencdk/context.h create mode 100644 src/daemon/https/opencdk/dummy.c create mode 100644 src/daemon/https/opencdk/filters.h create mode 100644 src/daemon/https/opencdk/kbnode.c create mode 100644 src/daemon/https/opencdk/keydb.c create mode 100644 src/daemon/https/opencdk/literal.c create mode 100644 src/daemon/https/opencdk/main.c create mode 100644 src/daemon/https/opencdk/main.h create mode 100644 src/daemon/https/opencdk/misc.c create mode 100644 src/daemon/https/opencdk/new-packet.c create mode 100644 src/daemon/https/opencdk/opencdk.h create mode 100644 src/daemon/https/opencdk/packet.h create mode 100644 src/daemon/https/opencdk/pubkey.c create mode 100644 src/daemon/https/opencdk/read-packet.c create mode 100644 src/daemon/https/opencdk/seskey.c create mode 100644 src/daemon/https/opencdk/sig-check.c create mode 100644 src/daemon/https/opencdk/stream.c create mode 100644 src/daemon/https/opencdk/stream.h create mode 100644 src/daemon/https/opencdk/types.h create mode 100644 src/daemon/https/opencdk/verify.c create mode 100644 src/daemon/https/opencdk/write-packet.c create mode 100644 src/daemon/https/openpgp/Makefile.am create mode 100644 src/daemon/https/openpgp/compat.c create mode 100644 src/daemon/https/openpgp/extras.c create mode 100644 src/daemon/https/openpgp/gnutls_extra.c create mode 100644 src/daemon/https/openpgp/gnutls_extra.h create mode 100644 src/daemon/https/openpgp/gnutls_ia.c create mode 100644 src/daemon/https/openpgp/gnutls_openpgp.c create mode 100644 src/daemon/https/openpgp/gnutls_openpgp.h create mode 100644 src/daemon/https/openpgp/openpgp.h create mode 100644 src/daemon/https/openpgp/pgp.c create mode 100644 src/daemon/https/openpgp/pgp_privkey.c create mode 100644 src/daemon/https/openpgp/pgp_verify.c create mode 100644 src/daemon/https/tests.c create mode 100644 src/daemon/https/tests.h create mode 100644 src/daemon/https/tls/Makefile.am create mode 100644 src/daemon/https/tls/auth_anon.c create mode 100644 src/daemon/https/tls/auth_anon.h create mode 100644 src/daemon/https/tls/auth_cert.c create mode 100644 src/daemon/https/tls/auth_cert.h create mode 100644 src/daemon/https/tls/auth_dh_common.c create mode 100644 src/daemon/https/tls/auth_dh_common.h create mode 100644 src/daemon/https/tls/auth_dhe.c create mode 100644 src/daemon/https/tls/auth_rsa.c create mode 100644 src/daemon/https/tls/auth_rsa_export.c create mode 100644 src/daemon/https/tls/debug.c create mode 100644 src/daemon/https/tls/debug.h create mode 100644 src/daemon/https/tls/defines.h create mode 100644 src/daemon/https/tls/ext_cert_type.c create mode 100644 src/daemon/https/tls/ext_cert_type.h create mode 100644 src/daemon/https/tls/ext_inner_application.c create mode 100644 src/daemon/https/tls/ext_inner_application.h create mode 100644 src/daemon/https/tls/ext_max_record.c create mode 100644 src/daemon/https/tls/ext_max_record.h create mode 100644 src/daemon/https/tls/ext_oprfi.c create mode 100644 src/daemon/https/tls/ext_oprfi.h create mode 100644 src/daemon/https/tls/ext_server_name.c create mode 100644 src/daemon/https/tls/ext_server_name.h create mode 100644 src/daemon/https/tls/gnutls.asn create mode 100644 src/daemon/https/tls/gnutls.pc create mode 100644 src/daemon/https/tls/gnutls_alert.c create mode 100644 src/daemon/https/tls/gnutls_algorithms.c create mode 100644 src/daemon/https/tls/gnutls_algorithms.h create mode 100644 src/daemon/https/tls/gnutls_anon_cred.c create mode 100644 src/daemon/https/tls/gnutls_asn1_tab.c create mode 100644 src/daemon/https/tls/gnutls_auth.c create mode 100644 src/daemon/https/tls/gnutls_auth.h create mode 100644 src/daemon/https/tls/gnutls_auth_int.h create mode 100644 src/daemon/https/tls/gnutls_buffer.h create mode 100644 src/daemon/https/tls/gnutls_buffers.c create mode 100644 src/daemon/https/tls/gnutls_buffers.h create mode 100644 src/daemon/https/tls/gnutls_cert.c create mode 100644 src/daemon/https/tls/gnutls_cert.h create mode 100644 src/daemon/https/tls/gnutls_cipher.c create mode 100644 src/daemon/https/tls/gnutls_cipher.h create mode 100644 src/daemon/https/tls/gnutls_cipher_int.c create mode 100644 src/daemon/https/tls/gnutls_cipher_int.h create mode 100644 src/daemon/https/tls/gnutls_compress.c create mode 100644 src/daemon/https/tls/gnutls_compress.h create mode 100644 src/daemon/https/tls/gnutls_compress_int.c create mode 100644 src/daemon/https/tls/gnutls_compress_int.h create mode 100644 src/daemon/https/tls/gnutls_constate.c create mode 100644 src/daemon/https/tls/gnutls_constate.h create mode 100644 src/daemon/https/tls/gnutls_datum.c create mode 100644 src/daemon/https/tls/gnutls_datum.h create mode 100644 src/daemon/https/tls/gnutls_db.c create mode 100644 src/daemon/https/tls/gnutls_db.h create mode 100644 src/daemon/https/tls/gnutls_dh.c create mode 100644 src/daemon/https/tls/gnutls_dh.h create mode 100644 src/daemon/https/tls/gnutls_dh_primes.c create mode 100644 src/daemon/https/tls/gnutls_errors.c create mode 100644 src/daemon/https/tls/gnutls_errors.h create mode 100644 src/daemon/https/tls/gnutls_extensions.c create mode 100644 src/daemon/https/tls/gnutls_extensions.h create mode 100644 src/daemon/https/tls/gnutls_extra_hooks.c create mode 100644 src/daemon/https/tls/gnutls_extra_hooks.h create mode 100644 src/daemon/https/tls/gnutls_global.c create mode 100644 src/daemon/https/tls/gnutls_global.h create mode 100644 src/daemon/https/tls/gnutls_handshake.c create mode 100644 src/daemon/https/tls/gnutls_handshake.h create mode 100644 src/daemon/https/tls/gnutls_hash_int.c create mode 100644 src/daemon/https/tls/gnutls_hash_int.h create mode 100644 src/daemon/https/tls/gnutls_int.h create mode 100644 src/daemon/https/tls/gnutls_kx.c create mode 100644 src/daemon/https/tls/gnutls_kx.h create mode 100644 src/daemon/https/tls/gnutls_mem.c create mode 100644 src/daemon/https/tls/gnutls_mem.h create mode 100644 src/daemon/https/tls/gnutls_mpi.c create mode 100644 src/daemon/https/tls/gnutls_mpi.h create mode 100644 src/daemon/https/tls/gnutls_num.c create mode 100644 src/daemon/https/tls/gnutls_num.h create mode 100644 src/daemon/https/tls/gnutls_pk.c create mode 100644 src/daemon/https/tls/gnutls_pk.h create mode 100644 src/daemon/https/tls/gnutls_priority.c create mode 100644 src/daemon/https/tls/gnutls_record.c create mode 100644 src/daemon/https/tls/gnutls_record.h create mode 100644 src/daemon/https/tls/gnutls_rsa_export.c create mode 100644 src/daemon/https/tls/gnutls_rsa_export.h create mode 100644 src/daemon/https/tls/gnutls_session.c create mode 100644 src/daemon/https/tls/gnutls_session.h create mode 100644 src/daemon/https/tls/gnutls_session_pack.c create mode 100644 src/daemon/https/tls/gnutls_session_pack.h create mode 100644 src/daemon/https/tls/gnutls_sig.c create mode 100644 src/daemon/https/tls/gnutls_sig.h create mode 100644 src/daemon/https/tls/gnutls_state.c create mode 100644 src/daemon/https/tls/gnutls_state.h create mode 100644 src/daemon/https/tls/gnutls_str.c create mode 100644 src/daemon/https/tls/gnutls_str.h create mode 100644 src/daemon/https/tls/gnutls_supplemental.c create mode 100644 src/daemon/https/tls/gnutls_supplemental.h create mode 100644 src/daemon/https/tls/gnutls_ui.c create mode 100644 src/daemon/https/tls/gnutls_v2_compat.c create mode 100644 src/daemon/https/tls/gnutls_v2_compat.h create mode 100644 src/daemon/https/tls/gnutls_x509.c create mode 100644 src/daemon/https/tls/gnutls_x509.h create mode 100644 src/daemon/https/tls/gnutlsxx.cpp create mode 100644 src/daemon/https/tls/io_debug.h create mode 100644 src/daemon/https/tls/libgnutls-config create mode 100644 src/daemon/https/tls/libgnutls.m4 create mode 100644 src/daemon/https/tls/libgnutls.vers create mode 100644 src/daemon/https/tls/libgnutlsxx.vers create mode 100644 src/daemon/https/tls/pkix.asn create mode 100644 src/daemon/https/tls/pkix_asn1_tab.c create mode 100644 src/daemon/https/tls/x509_b64.c create mode 100644 src/daemon/https/tls/x509_b64.h create mode 100644 src/daemon/https/tls_test.c create mode 100644 src/daemon/https/x509/Makefile.am create mode 100644 src/daemon/https/x509/common.c create mode 100644 src/daemon/https/x509/common.h create mode 100644 src/daemon/https/x509/crl.c create mode 100644 src/daemon/https/x509/crl_write.c create mode 100644 src/daemon/https/x509/crq.c create mode 100644 src/daemon/https/x509/crq.h create mode 100644 src/daemon/https/x509/dn.c create mode 100644 src/daemon/https/x509/dn.h create mode 100644 src/daemon/https/x509/dsa.c create mode 100644 src/daemon/https/x509/dsa.h create mode 100644 src/daemon/https/x509/extensions.c create mode 100644 src/daemon/https/x509/extensions.h create mode 100644 src/daemon/https/x509/mpi.c create mode 100644 src/daemon/https/x509/mpi.h create mode 100644 src/daemon/https/x509/output.c create mode 100644 src/daemon/https/x509/pkcs12.c create mode 100644 src/daemon/https/x509/pkcs12.h create mode 100644 src/daemon/https/x509/pkcs12_bag.c create mode 100644 src/daemon/https/x509/pkcs12_encr.c create mode 100644 src/daemon/https/x509/pkcs7.c create mode 100644 src/daemon/https/x509/pkcs7.h create mode 100644 src/daemon/https/x509/privkey.h create mode 100644 src/daemon/https/x509/privkey_pkcs8.c create mode 100644 src/daemon/https/x509/rfc2818.h create mode 100644 src/daemon/https/x509/rfc2818_hostname.c create mode 100644 src/daemon/https/x509/sign.c create mode 100644 src/daemon/https/x509/sign.h create mode 100644 src/daemon/https/x509/verify.h create mode 100644 src/daemon/https/x509/x509-api.texi create mode 100644 src/daemon/https/x509/x509.c create mode 100644 src/daemon/https/x509/x509.h create mode 100644 src/daemon/https/x509/x509_privkey.c create mode 100644 src/daemon/https/x509/x509_verify.c create mode 100644 src/daemon/https/x509/x509_write.c diff --git a/configure.ac b/configure.ac index 55bd209c..29caa965 100644 --- a/configure.ac +++ b/configure.ac @@ -121,7 +121,6 @@ LIBS=$SAVE_LIBS AC_SYS_LARGEFILE AC_FUNC_FSEEKO - # optional: have error messages? AC_MSG_CHECKING(--enable-messages argument) AC_ARG_ENABLE(messages, @@ -148,13 +147,29 @@ AC_ARG_ENABLE([HTTPS], [enable_HTTPS=$enableval], [enable_HTTPS="yes"]) AC_MSG_RESULT($enable_HTTPS) -if test "$enable_messages" = "yes" +if test "$enable_HTTPS" = "no" then - AC_DEFINE([HTTPS_SUPPORT],[0],[Include HTTPS support]) + AC_DEFINE([HTTPS_SUPPORT],[0],[disable HTTPS support]) else AC_DEFINE([HTTPS_SUPPORT],[1],[Include HTTPS support]) fi +# Symbols required by GNU_TLS +AC_DEFINE([ENABLE_MINITASN1],[1],[Include minitasn1 support]) +AC_DEFINE([ENABLE_OPENPGP],[1],[Include ENABLE_OPENPGP support]) +AC_DEFINE([GNULIB_GC_HMAC_SHA1],[1],[GNULIB_GC_HMAC_SHA1]) +AC_DEFINE([GNULIB_GC_RANDOM],[1],[GNULIB_GC_RANDOM]) +AC_DEFINE([ENABLE_PKI],[1],[Include ENABLE_OPENPGP support]) +AC_DEFINE([ENABLE_INCLUDED_OPENCDK],[1],[Include ENABLE_INCLUDED_OPENCDK support]) +# gnutls debug support +AC_DEFINE([DEBUG],[1],[Include gnutls debug message support]) +AC_DEFINE([C99_MACROS],[1],[Include gnutls debug message support]) + + +AC_DEFINE([ENABLE_OPENSSL],[0],[Include ENABLE_OPENSSL support]) +AC_DEFINE([HAVE_LD_OUTPUT_DEF],[0],[Include HAVE_LD_OUTPUT_DEF support]) +AC_DEFINE([HAVE_LD_VERSION_SCRIPT],[0],[Include HAVE_LD_VERSION_SCRIPT support]) + # Libgcrypt linkage AC_ARG_WITH(libgcrypt, @@ -170,12 +185,12 @@ AC_ARG_WITH(libgcrypt, LDFLAGS="-L$with_libgcrypt/lib $LDFLAGS" CPPFLAGS="-I$with_libgcrypt/include $CPPFLAGS" AC_CHECK_HEADERS(gcrypt.h, - # check for 'gnutls_global_init' in gnutls.so + # check for 'gcry_prime_generate' in gcrypt.la AC_CHECK_LIB(gcrypt,gcry_prime_generate, GCRYPT_LIB_PATH="$with_libgcrypt/lib" GCRYPT_LDFLAGS="-L$with_libgcrypt/lib" GCRYPT_CPPFLAGS="-I$with_libgcrypt/include" - gnutls=true)) + libgcrypt=true)) LDFLAGS=$SAVE_LDFLAGS CPPFLAGS=$SAVE_CPPFLAGS ;; @@ -201,11 +216,11 @@ AC_ARG_WITH(gnutls, LDFLAGS="-L$with_gnutls/lib $LDFLAGS" CPPFLAGS="-I$with_gnutls/include $CPPFLAGS" AC_CHECK_HEADERS(gnutls/gnutls.h, - # check for 'gnutls_global_init' in gnutls.so + # check for 'gnutls_global_init' in gnutls.la AC_CHECK_LIB(gnutls,gnutls_global_init, GNUTLS_LIB_PATH="$with_gnutls/lib" GNUTLS_LDFLAGS="-L$with_gnutls/lib" - GNUTLS_CPPFLAGS="-I$with_gnutls/include" + GNUTLS_CPPFLAGS="-I$with_gnutls/include/gnutls" gnutls=true)) LDFLAGS=$SAVE_LDFLAGS CPPFLAGS=$SAVE_CPPFLAGS @@ -231,6 +246,14 @@ m4/Makefile src/Makefile src/include/Makefile src/daemon/Makefile +src/daemon/https/Makefile +src/daemon/https/tls/Makefile +src/daemon/https/x509/Makefile +src/daemon/https/lgl/Makefile +src/daemon/https/openpgp/Makefile +src/daemon/https/opencdk/Makefile +src/daemon/https/minitasn1/Makefile +src/daemon/https/includes/Makefile src/examples/Makefile src/testcurl/Makefile src/testzzuf/Makefile]) @@ -240,3 +263,10 @@ if test "$curl" != 1 then AC_MSG_NOTICE([WARNING: libcurl not found, testcases cannot be built.]) fi + +AM_CONDITIONAL(ENABLE_MINITASN1, [test -n " " ] ) +AM_CONDITIONAL(ENABLE_OPENPGP, [test -n " " ] ) +AM_CONDITIONAL(ENABLE_OPENSSL, [test -n "" ] ) +AM_CONDITIONAL(HAVE_LD_OUTPUT_DEF, [test -n "" ] ) +AM_CONDITIONAL(HAVE_LD_VERSION_SCRIPT, [test -n "" ] ) +AM_CONDITIONAL(ENABLE_INCLUDED_OPENCDK, [test -n "" ] ) diff --git a/src/daemon/Makefile.am b/src/daemon/Makefile.am index 365c1cf1..302d6ece 100644 --- a/src/daemon/Makefile.am +++ b/src/daemon/Makefile.am @@ -1,6 +1,15 @@ -SUBDIRS = . +SUBDIRS = https . -INCLUDES = -I$(top_srcdir)/src/include +AM_CPPFLAGS = -I$(top_srcdir)/src/include \ +-I$(top_srcdir)/src/daemon \ +-I$(top_srcdir)/src/daemon/https/lgl \ +-I$(top_srcdir)/src/daemon/https/x509 \ +-I$(top_srcdir)/src/daemon/https/openpgp \ +-I$(top_srcdir)/src/daemon/https/opencdk \ +-I$(top_srcdir)/src/daemon/https/tls \ +-I$(top_srcdir)/src/daemon/https/includes \ +-I$(top_srcdir)/src/daemon/https/cfg \ +-I$(GCRYPT_CPPFLAGS) if HAVE_GNU_LD retaincommand=-Wl,--retain-symbols-file -Wl,$(srcdir)/SYMBOLS @@ -12,21 +21,21 @@ lib_LTLIBRARIES = \ libmicrohttpd.la libmicrohttpd_la_SOURCES = \ - connection.c connection.h \ - reason_phrase.c reason_phrase.h \ - daemon.c \ - internal.c internal.h \ - memorypool.c memorypool.h \ - plibc.h \ - postprocessor.c \ - response.c response.h +connection.c connection.h \ +reason_phrase.c reason_phrase.h \ +daemon.c \ +internal.c internal.h \ +memorypool.c memorypool.h \ +plibc.h \ +postprocessor.c \ +response.c response.h libmicrohttpd_la_LDFLAGS = \ - -export-dynamic -version-info 4:3:0 $(retaincommand) \ - -L$(GNUTLS_LIB_PATH) \ - -lgnutls -libmicrohttpd_la_CPPFLAGS = \ - $(GNUTLS_CPPFLAGS) + -export-dynamic -version-info 4:3:0 $(retaincommand) +libmicrohttpd_la_LIBADD = \ +https/libhttps.la + + check_PROGRAMS = \ postprocessor_test \ postprocessor_large_test \ @@ -44,7 +53,6 @@ postprocessor_test_SOURCES = \ postprocessor_test_LDADD = \ $(top_builddir)/src/daemon/libmicrohttpd.la - postprocessor_large_test_SOURCES = \ postprocessor_large_test.c postprocessor_large_test_LDADD = \ diff --git a/src/daemon/https/Makefile.am b/src/daemon/https/Makefile.am new file mode 100644 index 00000000..33f46f1b --- /dev/null +++ b/src/daemon/https/Makefile.am @@ -0,0 +1,34 @@ +SUBDIRS = minitasn1 opencdk openpgp lgl x509 tls . + +AM_CPPFLAGS = -I./includes \ +-I$(top_srcdir)/src/daemon/https/lgl \ +-I$(top_srcdir)/src/daemon/https/x509 \ +-I$(top_srcdir)/src/daemon/https/openpgp \ +-I$(top_srcdir)/src/daemon/https/opencdk \ +-I$(top_srcdir)/src/daemon/https/tls \ +-I$(top_srcdir)/src/daemon/https/cfg + +noinst_LTLIBRARIES = libhttps.la + +libhttps_la_SOURCES = \ +https_common.c \ +errcodes.c + +libhttps_la_LIBADD = \ +opencdk/libopencdk.la \ +openpgp/libopenpgp.la \ +x509/libx509.la \ +lgl/liblgl.la \ +tls/libtls.la \ +minitasn1/libasn1.la + +#noinst_PROGRAMS = errcodes +#errcodes_SOURCES = errcodes.c +#errcodes_LDADD = ../lib/libgnutls.la $(LIBGCRYPT_LIBS) $(LIBTASN1_LIBS) + +# gnutls_serv_SOURCES = serv.gaa serv-gaa.h serv-gaa.c list.h serv.c common.h common.c select.c +# srptool_SOURCES = crypt.gaa crypt-gaa.h crypt-gaa.c crypt.c +# gnutls_cli_debug_SOURCES = tls_test.gaa tls_test-gaa.h tls_test-gaa.c \ +# tls_test.c tests.h tests.c common.h common.c +# certtool_SOURCES = certtool.gaa certtool-gaa.h certtool-cfg.h \ +# certtool-gaa.c certtool.c prime.c certtool-cfg.c diff --git a/src/daemon/https/errcodes b/src/daemon/https/errcodes new file mode 100644 index 00000000..65774f43 --- /dev/null +++ b/src/daemon/https/errcodes @@ -0,0 +1,131 @@ +#! /bin/sh + +# errcodes - temporary wrapper script for .libs/errcodes +# Generated by ltmain.sh - GNU libtool 1.5.26 Debian 1.5.26-3 (1.1220.2.493 2008/02/01 16:58:18) +# +# The errcodes program cannot be directly executed until all the libtool +# libraries that it depends on are installed. +# +# This wrapper script should never be moved out of the build directory. +# If it is, it will not operate correctly. + +# Sed substitution that helps us do robust quoting. It backslashifies +# metacharacters that are still active within double-quoted strings. +Xsed='/bin/sed -e 1s/^X//' +sed_quote_subst='s/\([\\`\\"$\\\\]\)/\\\1/g' + +# Be Bourne compatible (taken from Autoconf:_AS_BOURNE_COMPATIBLE). +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac +fi +BIN_SH=xpg4; export BIN_SH # for Tru64 +DUALCASE=1; export DUALCASE # for MKS sh + +# The HP-UX ksh and POSIX shell print the target directory to stdout +# if CDPATH is set. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +relink_command="(cd /home/lama/workbench/programming/c/gnunet/gnutls-2.2.3/src; { test -z \"\${LIBRARY_PATH+set}\" || unset LIBRARY_PATH || { LIBRARY_PATH=; export LIBRARY_PATH; }; }; { test -z \"\${COMPILER_PATH+set}\" || unset COMPILER_PATH || { COMPILER_PATH=; export COMPILER_PATH; }; }; { test -z \"\${GCC_EXEC_PREFIX+set}\" || unset GCC_EXEC_PREFIX || { GCC_EXEC_PREFIX=; export GCC_EXEC_PREFIX; }; }; { test -z \"\${LD_RUN_PATH+set}\" || unset LD_RUN_PATH || { LD_RUN_PATH=; export LD_RUN_PATH; }; }; { test -z \"\${LD_LIBRARY_PATH+set}\" || unset LD_LIBRARY_PATH || { LD_LIBRARY_PATH=; export LD_LIBRARY_PATH; }; }; PATH=\"/usr/lib/distcc:/usr/local/bin:/usr/bin:/bin:/usr/games\"; export PATH; gcc -std=gnu99 -g -O2 -D_REENTRANT -D_THREAD_SAFE -pipe -g -O2 -D_REENTRANT -D_THREAD_SAFE -Wno-pointer-sign -o \$progdir/\$file errcodes.o ../lib/.libs/libgnutls.so /usr/lib/libgcrypt.so -L/usr/lib /usr/lib/libtasn1.so -Wl,--rpath -Wl,/home/lama/workbench/programming/c/gnunet/gnutls-2.2.3/lib/.libs -Wl,--rpath -Wl,/home/lama/workbench/programming/c/gnunet/gnutls-2.2.3/build/lib ) " + +# This environment variable determines our operation mode. +if test "$libtool_install_magic" = "%%%MAGIC variable%%%"; then + # install mode needs the following variable: + notinst_deplibs=' ../lib/libgnutls.la' +else + # When we are sourced in execute mode, $file and $echo are already set. + if test "$libtool_execute_magic" != "%%%MAGIC variable%%%"; then + echo="echo" + file="$0" + # Make sure echo works. + if test "X$1" = X--no-reexec; then + # Discard the --no-reexec flag, and continue. + shift + elif test "X`($echo '\t') 2>/dev/null`" = 'X\t'; then + # Yippee, $echo works! + : + else + # Restart under the correct shell, and then maybe $echo will work. + exec /bin/sh "$0" --no-reexec ${1+"$@"} + fi + fi + + # Find the directory that this script lives in. + thisdir=`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'` + test "x$thisdir" = "x$file" && thisdir=. + + # Follow symbolic links until we get to the real thisdir. + file=`ls -ld "$file" | /bin/sed -n 's/.*-> //p'` + while test -n "$file"; do + destdir=`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'` + + # If there was a directory component, then change thisdir. + if test "x$destdir" != "x$file"; then + case "$destdir" in + [\\/]* | [A-Za-z]:[\\/]*) thisdir="$destdir" ;; + *) thisdir="$thisdir/$destdir" ;; + esac + fi + + file=`$echo "X$file" | $Xsed -e 's%^.*/%%'` + file=`ls -ld "$thisdir/$file" | /bin/sed -n 's/.*-> //p'` + done + + # Try to get the absolute directory name. + absdir=`cd "$thisdir" && pwd` + test -n "$absdir" && thisdir="$absdir" + + program=lt-'errcodes' + progdir="$thisdir/.libs" + + if test ! -f "$progdir/$program" || \ + { file=`ls -1dt "$progdir/$program" "$progdir/../$program" 2>/dev/null | /bin/sed 1q`; \ + test "X$file" != "X$progdir/$program"; }; then + + file="$$-$program" + + if test ! -d "$progdir"; then + mkdir "$progdir" + else + rm -f "$progdir/$file" + fi + + # relink executable if necessary + if test -n "$relink_command"; then + if relink_command_output=`eval $relink_command 2>&1`; then : + else + echo "$relink_command_output" >&2 + rm -f "$progdir/$file" + exit 1 + fi + fi + + mv -f "$progdir/$file" "$progdir/$program" 2>/dev/null || + { rm -f "$progdir/$program"; + mv -f "$progdir/$file" "$progdir/$program"; } + rm -f "$progdir/$file" + fi + + if test -f "$progdir/$program"; then + if test "$libtool_execute_magic" != "%%%MAGIC variable%%%"; then + # Run the actual program with our arguments. + + exec "$progdir/$program" ${1+"$@"} + + $echo "$0: cannot exec $program $*" + exit 1 + fi + else + # The program doesn't exist. + $echo "$0: error: \`$progdir/$program' does not exist" 1>&2 + $echo "This script is just a wrapper for $program." 1>&2 + echo "See the libtool documentation for more information." 1>&2 + exit 1 + fi +fi diff --git a/src/daemon/https/errcodes.c b/src/daemon/https/errcodes.c new file mode 100644 index 00000000..96d04240 --- /dev/null +++ b/src/daemon/https/errcodes.c @@ -0,0 +1,65 @@ +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include + +const char *_gnutls_strerror (int); + +typedef struct +{ + char name[128]; + int error_index; +} error_name; + + +static int +compar (const void *_n1, const void *_n2) +{ + const error_name *n1 = (const error_name *) _n1, + *n2 = (const error_name *) _n2; + return strcmp (n1->name, n2->name); +} + +//int +//main (int argc, char *argv[]) +//{ +// int i, j; +// const char *desc; +// const char *_name; +// error_name names_to_sort[400]; /* up to 400 names */ +// +// printf ("@table @code\n"); +// +// memset (names_to_sort, 0, sizeof (names_to_sort)); +// j = 0; +// for (i = 0; i > -400; i--) +// { +// _name = _gnutls_strerror (i); +// if (_name == NULL) +// continue; +// +// strcpy (names_to_sort[j].name, _name); +// names_to_sort[j].error_index = i; +// j++; +// } +// +// qsort (names_to_sort, j, sizeof (error_name), compar); +// +// for (i = 0; i < j; i++) +// { +// _name = names_to_sort[i].name; +// desc = gnutls_strerror (names_to_sort[i].error_index); +// if (desc == NULL || _name == NULL) +// continue; +// +// printf ("@item %s:\n%s\n\n", _name, desc); +// } +// +// printf ("@end table\n"); +// +// return 0; +//} diff --git a/src/daemon/https/https_common.c b/src/daemon/https/https_common.c new file mode 100644 index 00000000..748bc5bc --- /dev/null +++ b/src/daemon/https/https_common.c @@ -0,0 +1,877 @@ +/* + * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * GNUTLS is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GNUTLS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +//#include "openpgp.h" +#include +#include "https_common.h" + +#define TEST_STRING +#define SU(x) (x!=NULL?x:"Unknown") + +// TODO clean - originaly from tls_test extern int verbose; +int print_cert; +int verbose = 0; + +static char buffer[5 * 1024]; + +#define PRINTX(x,y) if (y[0]!=0) printf(" # %s %s\n", x, y) +#define PRINT_PGP_NAME(X) PRINTX( "NAME:", name) + +const char str_unknown[] = "(unknown)"; + +/* Hex encodes the given data. + */ +const char * +raw_to_string (const unsigned char *raw, size_t raw_size) +{ + static char buf[1024]; + size_t i; + if (raw_size == 0) + return NULL; + + if (raw_size * 3 + 1 >= sizeof (buf)) + return NULL; + + for (i = 0; i < raw_size; i++) + { + sprintf (&(buf[i * 3]), "%02X%s", raw[i], (i == raw_size - 1) ? "" + : ":"); + } + buf[sizeof (buf) - 1] = '\0'; + + return buf; +} + +static const char * +my_ctime (const time_t * tv) +{ + static char buf[256]; + struct tm *tp; + + if (((tp = localtime (tv)) == NULL) || (!strftime (buf, sizeof buf, + "%a %b %e %H:%M:%S %Z %Y\n", + tp))) + strcpy (buf, str_unknown); /* make sure buf text isn't garbage */ + + return buf; + +} + +void +print_x509_info (gnutls_session_t session, const char *hostname) +{ + gnutls_x509_crt_t crt; + const gnutls_datum_t *cert_list; + unsigned int cert_list_size = 0; + int ret; + char digest[20]; + char serial[40]; + char dn[256]; + size_t dn_size; + size_t digest_size = sizeof (digest); + unsigned int j; + size_t serial_size = sizeof (serial); + const char *print; + const char *cstr; + unsigned int bits, algo; + time_t expiret, activet; + + cert_list = gnutls_certificate_get_peers (session, &cert_list_size); + + if (cert_list_size == 0) + { + fprintf (stderr, "No certificates found!\n"); + return; + } + + printf (" - Got a certificate list of %d certificates.\n\n", + cert_list_size); + + for (j = 0; j < (unsigned int) cert_list_size; j++) + { + + gnutls_x509_crt_init (&crt); + ret = gnutls_x509_crt_import (crt, &cert_list[j], GNUTLS_X509_FMT_DER); + if (ret < 0) + { + fprintf (stderr, "Decoding error: %s\n", gnutls_strerror (ret)); + return; + } + + printf (" - Certificate[%d] info:\n", j); + + if (print_cert) + { + size_t size; + + size = sizeof (buffer); + + ret = + gnutls_x509_crt_export (crt, GNUTLS_X509_FMT_PEM, buffer, &size); + if (ret < 0) + { + fprintf (stderr, "Encoding error: %s\n", gnutls_strerror (ret)); + return; + } + fputs ("\n", stdout); + fputs (buffer, stdout); + fputs ("\n", stdout); + } + + if (j == 0 && hostname != NULL) + { /* Check the hostname of the first certificate + * if it matches the name of the host we + * connected to. + */ + if (gnutls_x509_crt_check_hostname (crt, hostname) == 0) + { + printf + (" # The hostname in the certificate does NOT match '%s'.\n", + hostname); + } + else + { + printf (" # The hostname in the certificate matches '%s'.\n", + hostname); + } + } + + expiret = gnutls_x509_crt_get_expiration_time (crt); + activet = gnutls_x509_crt_get_activation_time (crt); + + printf (" # valid since: %s", my_ctime (&activet)); + printf (" # expires at: %s", my_ctime (&expiret)); + + /* Print the serial number of the certificate. + */ + if (verbose + && gnutls_x509_crt_get_serial (crt, serial, &serial_size) >= 0) + { + print = raw_to_string (serial, serial_size); + if (print != NULL) + printf (" # serial number: %s\n", print); + } + + /* Print the fingerprint of the certificate + */ + digest_size = sizeof (digest); + if ((ret = gnutls_x509_crt_get_fingerprint (crt, GNUTLS_DIG_MD5, digest, + &digest_size)) < 0) + { + fprintf (stderr, + "Error in fingerprint calculation: %s\n", + gnutls_strerror (ret)); + } + else + { + print = raw_to_string (digest, digest_size); + if (print != NULL) + printf (" # fingerprint: %s\n", print); + } + + /* Print the version of the X.509 + * certificate. + */ + if (verbose) + { + printf (" # version: #%d\n", gnutls_x509_crt_get_version (crt)); + + bits = 0; + algo = gnutls_x509_crt_get_pk_algorithm (crt, &bits); + printf (" # public key algorithm: "); + + cstr = SU (gnutls_pk_algorithm_get_name (algo)); + printf ("%s (%d bits)\n", cstr, bits); + +#ifdef ENABLE_PKI + if (algo == GNUTLS_PK_RSA) + { + gnutls_datum_t e, m; + + ret = gnutls_x509_crt_get_pk_rsa_raw (crt, &m, &e); + if (ret >= 0) + { + print = SU (raw_to_string (e.data, e.size)); + printf (" # e [%d bits]: %s\n", e.size * 8, print); + + print = SU (raw_to_string (m.data, m.size)); + printf (" # m [%d bits]: %s\n", m.size * 8, print); + + gnutls_free (e.data); + gnutls_free (m.data); + } + } +#endif + } + + dn_size = sizeof (dn); + ret = gnutls_x509_crt_get_dn (crt, dn, &dn_size); + if (ret >= 0) + printf (" # Subject's DN: %s\n", dn); + + dn_size = sizeof (dn); + ret = gnutls_x509_crt_get_issuer_dn (crt, dn, &dn_size); + if (ret >= 0) + printf (" # Issuer's DN: %s\n", dn); + + gnutls_x509_crt_deinit (crt); + + printf ("\n"); + + } + +} + +#ifdef ENABLE_OPENPGP + +void +print_openpgp_info (gnutls_session_t session, const char *hostname) +{ + + char digest[20]; + size_t digest_size = sizeof (digest); + int ret; + const char *print; + const char *cstr; + char name[256]; + size_t name_len = sizeof (name); + gnutls_openpgp_crt_t crt; + const gnutls_datum_t *cert_list; + int cert_list_size = 0; + time_t expiret; + time_t activet; + + cert_list = gnutls_certificate_get_peers (session, &cert_list_size); + + if (cert_list_size > 0) + { + unsigned int algo, bits; + + gnutls_openpgp_crt_init (&crt); + ret = gnutls_openpgp_crt_import (crt, &cert_list[0], + GNUTLS_OPENPGP_FMT_RAW); + if (ret < 0) + { + fprintf (stderr, "Decoding error: %s\n", gnutls_strerror (ret)); + return; + } + + if (print_cert) + { + size_t size; + + size = sizeof (buffer); + + ret = gnutls_openpgp_crt_export (crt, GNUTLS_OPENPGP_FMT_BASE64, + buffer, &size); + if (ret < 0) + { + fprintf (stderr, "Encoding error: %s\n", gnutls_strerror (ret)); + return; + } + fputs ("\n", stdout); + fputs (buffer, stdout); + fputs ("\n", stdout); + } + + if (hostname != NULL) + { /* Check the hostname of the first certificate + * if it matches the name of the host we + * connected to. + */ + if (gnutls_openpgp_crt_check_hostname (crt, hostname) == 0) + { + printf (" # The hostname in the key does NOT match '%s'.\n", + hostname); + } + else + { + printf (" # The hostname in the key matches '%s'.\n", hostname); + } + } + + activet = gnutls_openpgp_crt_get_creation_time (crt); + expiret = gnutls_openpgp_crt_get_expiration_time (crt); + + printf (" # Key was created at: %s", my_ctime (&activet)); + printf (" # Key expires: "); + if (expiret != 0) + printf ("%s", my_ctime (&expiret)); + else + printf ("Never\n"); + + if (gnutls_openpgp_crt_get_fingerprint (crt, digest, &digest_size) >= 0) + { + print = raw_to_string (digest, digest_size); + + printf (" # PGP Key version: %d\n", + gnutls_openpgp_crt_get_version (crt)); + + bits = 0; + algo = gnutls_openpgp_crt_get_pk_algorithm (crt, &bits); + + printf (" # PGP Key public key algorithm: "); + cstr = SU (gnutls_pk_algorithm_get_name (algo)); + printf ("%s (%d bits)\n", cstr, bits); + + if (print != NULL) + printf (" # PGP Key fingerprint: %s\n", print); + + name_len = sizeof (name); + if (gnutls_openpgp_crt_get_name (crt, 0, name, &name_len) < 0) + { + fprintf (stderr, "Could not extract name\n"); + } + else + { + PRINT_PGP_NAME (name); + } + + } + + gnutls_openpgp_crt_deinit (crt); + + } +} + +#endif + +void +print_cert_vrfy (gnutls_session_t session) +{ + int rc; + unsigned int status; + + rc = gnutls_certificate_verify_peers2 (session, &status); + printf ("\n"); + + if (rc == GNUTLS_E_NO_CERTIFICATE_FOUND) + { + printf ("- Peer did not send any certificate.\n"); + return; + } + + if (rc < 0) + { + printf ("- Could not verify certificate (err: %s)\n", + gnutls_strerror (rc)); + return; + } + + if (gnutls_certificate_type_get (session) == GNUTLS_CRT_X509) + { + if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) + printf ("- Peer's certificate issuer is unknown\n"); + if (status & GNUTLS_CERT_INVALID) + printf ("- Peer's certificate is NOT trusted\n"); + else + printf ("- Peer's certificate is trusted\n"); + } + else + { + if (status & GNUTLS_CERT_INVALID) + printf ("- Peer's key is invalid\n"); + else + printf ("- Peer's key is valid\n"); + if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) + printf ("- Could not find a signer of the peer's key\n"); + } +} + +int +print_info (gnutls_session_t session, const char *hostname) +{ + const char *tmp; + gnutls_credentials_type_t cred; + gnutls_kx_algorithm_t kx; + + /* print the key exchange's algorithm name + */ + kx = gnutls_kx_get (session); + + cred = gnutls_auth_get_type (session); + switch (cred) + { +#ifdef ENABLE_ANON + case GNUTLS_CRD_ANON: + printf ("- Anonymous DH using prime of %d bits, secret key " + "of %d bits, and peer's public key is %d bits.\n", + gnutls_dh_get_prime_bits (session), + gnutls_dh_get_secret_bits (session), + gnutls_dh_get_peers_public_bits (session)); + break; +#endif +#ifdef ENABLE_SRP + case GNUTLS_CRD_SRP: + /* This should be only called in server + * side. + */ + if (gnutls_srp_server_get_username (session) != NULL) + printf ("- SRP authentication. Connected as '%s'\n", + gnutls_srp_server_get_username (session)); + break; +#endif +#ifdef ENABLE_PSK + case GNUTLS_CRD_PSK: + /* This should be only called in server + * side. + */ + if (gnutls_psk_server_get_username (session) != NULL) + printf ("- PSK authentication. Connected as '%s'\n", + gnutls_psk_server_get_username (session)); + if (kx == GNUTLS_KX_DHE_PSK) + { + printf ("- DH using prime of %d bits, secret key " + "of %d bits, and peer's public key is %d bits.\n", + gnutls_dh_get_prime_bits (session), + gnutls_dh_get_secret_bits (session), + gnutls_dh_get_peers_public_bits (session)); + } + break; +#endif + case GNUTLS_CRD_IA: + printf ("- TLS/IA authentication\n"); + break; + case GNUTLS_CRD_CERTIFICATE: + { + char dns[256]; + size_t dns_size = sizeof (dns); + unsigned int type; + + /* This fails in client side */ + if (gnutls_server_name_get (session, dns, &dns_size, &type, 0) == 0) + { + printf ("- Given server name[%d]: %s\n", type, dns); + } + } + + if (kx == GNUTLS_KX_DHE_RSA || kx == GNUTLS_KX_DHE_DSS) + { + printf ("- Ephemeral DH using prime of %d bits, secret key " + "of %d bits, and peer's public key is %d bits.\n", + gnutls_dh_get_prime_bits (session), + gnutls_dh_get_secret_bits (session), + gnutls_dh_get_peers_public_bits (session)); + } + + print_cert_info (session, hostname); + + print_cert_vrfy (session); + + } + + tmp = SU (gnutls_protocol_get_name (gnutls_protocol_get_version (session))); + printf ("- Version: %s\n", tmp); + + tmp = SU (gnutls_kx_get_name (kx)); + printf ("- Key Exchange: %s\n", tmp); + + tmp = SU (gnutls_cipher_get_name (gnutls_cipher_get (session))); + printf ("- Cipher: %s\n", tmp); + + tmp = SU (gnutls_mac_get_name (gnutls_mac_get (session))); + printf ("- MAC: %s\n", tmp); + + tmp = SU (gnutls_compression_get_name (gnutls_compression_get (session))); + printf ("- Compression: %s\n", tmp); + + if (verbose) + { + char id[32]; + size_t id_size = sizeof (id); + gnutls_session_get_id (session, id, &id_size); + printf ("- Session ID: %s\n", raw_to_string (id, id_size)); + } + + fflush (stdout); + + return 0; +} + +void +print_cert_info (gnutls_session_t session, const char *hostname) +{ + + if (gnutls_certificate_client_get_request_status (session) != 0) + printf ("- Server has requested a certificate.\n"); + + printf ("- Certificate type: "); + switch (gnutls_certificate_type_get (session)) + { + case GNUTLS_CRT_X509: + printf ("X.509\n"); + print_x509_info (session, hostname); + break; +#ifdef ENABLE_OPENPGP + case GNUTLS_CRT_OPENPGP: + printf ("OpenPGP\n"); + print_openpgp_info (session, hostname); + break; +#endif + } +} + +void +print_list (int verbose) +{ + { + size_t i; + const char *name; + char id[2]; + gnutls_kx_algorithm_t kx; + gnutls_cipher_algorithm_t cipher; + gnutls_mac_algorithm_t mac; + gnutls_protocol_t version; + + printf ("Cipher suites:\n"); + for (i = 0; (name = gnutls_cipher_suite_info (i, id, &kx, &cipher, &mac, + &version)); i++) + { + printf ("%-50s\t0x%02x, 0x%02x\t%s\n", name, (unsigned char) id[0], + (unsigned char) id[1], gnutls_protocol_get_name (version)); + if (verbose) + printf ("\tKey exchange: %s\n\tCipher: %s\n\tMAC: %s\n\n", + gnutls_kx_get_name (kx), gnutls_cipher_get_name (cipher), + gnutls_mac_get_name (mac)); + } + } + + { + const gnutls_certificate_type_t *p = gnutls_certificate_type_list (); + + printf ("Certificate types: "); + for (; *p; p++) + { + printf ("%s", gnutls_certificate_type_get_name (*p)); + if (*(p + 1)) + printf (", "); + else + printf ("\n"); + } + } + + { + const gnutls_protocol_t *p = gnutls_protocol_list (); + + printf ("Protocols: "); + for (; *p; p++) + { + printf ("%s", gnutls_protocol_get_name (*p)); + if (*(p + 1)) + printf (", "); + else + printf ("\n"); + } + } + + { + const gnutls_cipher_algorithm_t *p = gnutls_cipher_list (); + + printf ("Ciphers: "); + for (; *p; p++) + { + printf ("%s", gnutls_cipher_get_name (*p)); + if (*(p + 1)) + printf (", "); + else + printf ("\n"); + } + } + + { + const gnutls_mac_algorithm_t *p = gnutls_mac_list (); + + printf ("MACs: "); + for (; *p; p++) + { + printf ("%s", gnutls_mac_get_name (*p)); + if (*(p + 1)) + printf (", "); + else + printf ("\n"); + } + } + + { + const gnutls_kx_algorithm_t *p = gnutls_kx_list (); + + printf ("Key exchange algorithms: "); + for (; *p; p++) + { + printf ("%s", gnutls_kx_get_name (*p)); + if (*(p + 1)) + printf (", "); + else + printf ("\n"); + } + } + + { + const gnutls_compression_method_t *p = gnutls_compression_list (); + + printf ("Compression: "); + for (; *p; p++) + { + printf ("%s", gnutls_compression_get_name (*p)); + if (*(p + 1)) + printf (", "); + else + printf ("\n"); + } + } +} + +void +print_license (void) +{ + fputs ("\nCopyright (C) 2004,2005,2006,2007 Free Software Foundation\n" + "This program is free software; you can redistribute it and/or modify \n" + "it under the terms of the GNU General Public License as published by \n" + "the Free Software Foundation; either version 3 of the License, or \n" + "(at your option) any later version. \n" "\n" + "This program is distributed in the hope that it will be useful, \n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of \n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the \n" + "GNU General Public License for more details. \n" "\n" + "You should have received a copy of the GNU General Public License \n" + "along with this program; if not, write to the Free Software \n" + "Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n\n", + stdout); +} + +static int depr_printed = 0; +#define DEPRECATED if (depr_printed==0) { \ + fprintf(stderr, "This method of specifying algorithms is deprecated. Please use the --priority option.\n"); \ + depr_printed = 1; \ + } + +void +parse_protocols (char **protocols, int protocols_size, int *protocol_priority) +{ + int i, j; + + if (protocols != NULL && protocols_size > 0) + { + DEPRECATED; + + for (j = i = 0; i < protocols_size; i++) + { + if (strncasecmp (protocols[i], "SSL", 3) == 0) + protocol_priority[j++] = GNUTLS_SSL3; + else if (strncasecmp (protocols[i], "TLS1.1", 6) == 0) + protocol_priority[j++] = GNUTLS_TLS1_1; + else if (strncasecmp (protocols[i], "TLS1.2", 6) == 0) + protocol_priority[j++] = GNUTLS_TLS1_2; + else if (strncasecmp (protocols[i], "TLS", 3) == 0) + protocol_priority[j++] = GNUTLS_TLS1_0; + else + fprintf (stderr, "Unknown protocol: '%s'\n", protocols[i]); + } + protocol_priority[j] = 0; + } +} + +void +parse_ciphers (char **ciphers, int nciphers, int *cipher_priority) +{ + int j, i; + + if (ciphers != NULL && nciphers > 0) + { + DEPRECATED; + for (j = i = 0; i < nciphers; i++) + { + if (strncasecmp (ciphers[i], "AES-2", 5) == 0) + cipher_priority[j++] = GNUTLS_CIPHER_AES_256_CBC; + else if (strncasecmp (ciphers[i], "AES", 3) == 0) + cipher_priority[j++] = GNUTLS_CIPHER_AES_128_CBC; + else if (strncasecmp (ciphers[i], "3DE", 3) == 0) + cipher_priority[j++] = GNUTLS_CIPHER_3DES_CBC; + else if (strcasecmp (ciphers[i], "ARCFOUR-40") == 0) + cipher_priority[j++] = GNUTLS_CIPHER_ARCFOUR_40; + else if (strcasecmp (ciphers[i], "ARCFOUR") == 0) + cipher_priority[j++] = GNUTLS_CIPHER_ARCFOUR_128; +#ifdef ENABLE_CAMELLIA + else if (strncasecmp (ciphers[i], "CAMELLIA-2", 10) == 0) + cipher_priority[j++] = GNUTLS_CIPHER_CAMELLIA_256_CBC; + else if (strncasecmp (ciphers[i], "CAM", 3) == 0) + cipher_priority[j++] = GNUTLS_CIPHER_CAMELLIA_128_CBC; +#endif + else if (strncasecmp (ciphers[i], "NUL", 3) == 0) + cipher_priority[j++] = GNUTLS_CIPHER_NULL; + else + fprintf (stderr, "Unknown cipher: '%s'\n", ciphers[i]); + } + cipher_priority[j] = 0; + } +} + +void +parse_macs (char **macs, int nmacs, int *mac_priority) +{ + int i, j; + + if (macs != NULL && nmacs > 0) + { + DEPRECATED; + for (j = i = 0; i < nmacs; i++) + { + if (strncasecmp (macs[i], "MD5", 3) == 0) + mac_priority[j++] = GNUTLS_MAC_MD5; + else if (strncasecmp (macs[i], "SHA256", 6) == 0) + mac_priority[j++] = GNUTLS_MAC_SHA256; + else if (strncasecmp (macs[i], "SHA", 3) == 0) + mac_priority[j++] = GNUTLS_MAC_SHA1; + else + fprintf (stderr, "Unknown MAC: '%s'\n", macs[i]); + } + mac_priority[j] = 0; + } +} + +void +parse_ctypes (char **ctype, int nctype, int *cert_type_priority) +{ + int i, j; + + if (ctype != NULL && nctype > 0) + { + DEPRECATED; + for (j = i = 0; i < nctype; i++) + { + if (strncasecmp (ctype[i], "OPE", 3) == 0) + cert_type_priority[j++] = GNUTLS_CRT_OPENPGP; + else if (strncasecmp (ctype[i], "X", 1) == 0) + cert_type_priority[j++] = GNUTLS_CRT_X509; + else + fprintf (stderr, "Unknown certificate type: '%s'\n", ctype[i]); + } + cert_type_priority[j] = 0; + } +} + +void +parse_kx (char **kx, int nkx, int *kx_priority) +{ + int i, j; + + if (kx != NULL && nkx > 0) + { + DEPRECATED; + for (j = i = 0; i < nkx; i++) + { + if (strcasecmp (kx[i], "SRP") == 0) + kx_priority[j++] = GNUTLS_KX_SRP; + else if (strcasecmp (kx[i], "SRP-RSA") == 0) + kx_priority[j++] = GNUTLS_KX_SRP_RSA; + else if (strcasecmp (kx[i], "SRP-DSS") == 0) + kx_priority[j++] = GNUTLS_KX_SRP_DSS; + else if (strcasecmp (kx[i], "RSA") == 0) + kx_priority[j++] = GNUTLS_KX_RSA; + else if (strcasecmp (kx[i], "PSK") == 0) + kx_priority[j++] = GNUTLS_KX_PSK; + else if (strcasecmp (kx[i], "DHE-PSK") == 0) + kx_priority[j++] = GNUTLS_KX_DHE_PSK; + else if (strcasecmp (kx[i], "RSA-EXPORT") == 0) + kx_priority[j++] = GNUTLS_KX_RSA_EXPORT; + else if (strncasecmp (kx[i], "DHE-RSA", 7) == 0) + kx_priority[j++] = GNUTLS_KX_DHE_RSA; + else if (strncasecmp (kx[i], "DHE-DSS", 7) == 0) + kx_priority[j++] = GNUTLS_KX_DHE_DSS; + else if (strncasecmp (kx[i], "ANON", 4) == 0) + kx_priority[j++] = GNUTLS_KX_ANON_DH; + else + fprintf (stderr, "Unknown key exchange: '%s'\n", kx[i]); + } + kx_priority[j] = 0; + } +} + +void +parse_comp (char **comp, int ncomp, int *comp_priority) +{ + int i, j; + + if (comp != NULL && ncomp > 0) + { + DEPRECATED; + for (j = i = 0; i < ncomp; i++) + { + if (strncasecmp (comp[i], "NUL", 3) == 0) + comp_priority[j++] = GNUTLS_COMP_NULL; + else if (strncasecmp (comp[i], "ZLI", 3) == 0) + comp_priority[j++] = GNUTLS_COMP_DEFLATE; + else if (strncasecmp (comp[i], "DEF", 3) == 0) + comp_priority[j++] = GNUTLS_COMP_DEFLATE; + else if (strncasecmp (comp[i], "LZO", 3) == 0) + comp_priority[j++] = GNUTLS_COMP_LZO; + else + fprintf (stderr, "Unknown compression: '%s'\n", comp[i]); + } + comp_priority[j] = 0; + } +} + +void +sockets_init (void) +{ +#ifdef _WIN32 + WORD wVersionRequested; + WSADATA wsaData; + + wVersionRequested = MAKEWORD (1, 1); + if (WSAStartup (wVersionRequested, &wsaData) != 0) + { + perror ("WSA_STARTUP_ERROR"); + } +#endif +} + +/* converts a service name or a port (in string) to a + * port number. The protocol is assumed to be TCP. + * + * returns -1 on error; + */ +int +service_to_port (const char *service) +{ + int port; + struct servent *server_port; + + port = atoi (service); + if (port != 0) + return port; + + server_port = getservbyname (service, "tcp"); + if (server_port == NULL) + { + perror ("getservbyname()"); + return (-1); + } + + return ntohs (server_port->s_port); + +} diff --git a/src/daemon/https/https_common.h b/src/daemon/https/https_common.h new file mode 100644 index 00000000..3ccd2cb1 --- /dev/null +++ b/src/daemon/https/https_common.h @@ -0,0 +1,40 @@ +#define PORT 5556 +#define SERVER "127.0.0.1" + +#include +#include + +#include +#include +#ifdef _WIN32 +# include +# include +# define close closesocket +#else +# include +# include +# include +# include +#endif + +/* the number of elements in the priority structures. + */ +#define PRI_MAX 16 + +extern const char str_unknown[]; + +int print_info (gnutls_session_t state, const char *hostname); +void print_cert_info (gnutls_session_t state, const char *hostname); +void print_list (int verbose); + +void parse_comp (char **comp, int ncomp, int *comp_priority); +void parse_kx (char **kx, int nkx, int *kx_priority); +void parse_ctypes (char **ctype, int nctype, int *cert_type_priority); +void parse_macs (char **macs, int nmacs, int *mac_priority); +void parse_ciphers (char **ciphers, int nciphers, int *cipher_priority); +void parse_protocols (char **protocols, int protocols_size, + int *protocol_priority); +const char *raw_to_string (const unsigned char *raw, size_t raw_size); +int service_to_port (const char *service); + +void sockets_init (void); diff --git a/src/daemon/https/includes/Makefile.am b/src/daemon/https/includes/Makefile.am new file mode 100644 index 00000000..b0c6ada1 --- /dev/null +++ b/src/daemon/https/includes/Makefile.am @@ -0,0 +1,3 @@ +AM_CPPFLAGS = -I$(top_srcdir)/src/https/includes + +lib_LTLIBRARIES = libmicrohttpd.la diff --git a/src/daemon/https/includes/compat.h b/src/daemon/https/includes/compat.h new file mode 100644 index 00000000..32f5d2bd --- /dev/null +++ b/src/daemon/https/includes/compat.h @@ -0,0 +1,98 @@ +/* Typedefs to be fully compatible with the types of + * GnuTLS 1.0.x. + */ + +#include "gnutls.h" + +#ifndef GCOMPAT_H +# define GCOMPAT_H + +#ifdef __GNUC__ + +#define _GNUTLS_GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) + +#if _GNUTLS_GCC_VERSION >= 30100 +#define _GNUTLS_GCC_ATTR_DEPRECATED __attribute__ ((__deprecated__)) +#endif + +#endif /* __GNUC__ */ + +#ifndef _GNUTLS_GCC_ATTR_DEPRECATED +#define _GNUTLS_GCC_ATTR_DEPRECATED +#endif + +#define gnutls_cipher_algorithm gnutls_cipher_algorithm_t +#define gnutls_kx_algorithm gnutls_kx_algorithm_t +#define gnutls_paramsype gnutls_paramsype_t +#define gnutls_mac_algorithm gnutls_mac_algorithm_t +#define gnutls_digest_algorithm gnutls_digest_algorithm_t +#define gnutls_compression_method gnutls_compression_method_t +#define gnutls_connection_end gnutls_connection_end_t +#define gnutls_credentialsype gnutls_credentialsype_t +#define gnutls_certificateype gnutls_certificateype_t +#define gnutls_x509_crt_fmt gnutls_x509_crt_fmt_t +#define gnutls_openpgp_key_fmt gnutls_openpgp_key_fmt_t +#define gnutls_pk_algorithm gnutls_pk_algorithm_t +#define gnutls_sign_algorithm gnutls_sign_algorithm_t +#define gnutls_server_name gnutls_server_nameype_t +#define gnutls_protocol gnutls_protocol_version_t +#define gnutls_close_request gnutls_close_request_t +#define gnutls_openpgp_key_status gnutls_openpgp_key_status_t +#define gnutls_certificate_request gnutls_certificate_request_t +#define gnutls_certificate_status gnutls_certificate_status_t +#define gnutls_session gnutls_session_t +#define gnutls_alert_level gnutls_alert_level_t +#define gnutls_alert_description gnutls_alert_description_t +#define gnutls_x509_subject_alt_name gnutls_x509_subject_alt_name_t +#define gnutls_openpgp_key gnutls_openpgp_key_t +#define gnutls_openpgp_privkey gnutls_openpgp_privkey_t +#define gnutls_openpgp_keyring gnutls_openpgp_keyring_t +#define gnutls_x509_crt gnutls_x509_crt_t +#define gnutls_x509_privkey gnutls_x509_privkey_t +#define gnutls_x509_crl gnutls_x509_crl_t +#define gnutls_pkcs7 gnutls_pkcs7_t +#define gnutls_x509_crq gnutls_x509_crq_t +#define gnutls_pkcs_encrypt_flags gnutls_pkcs_encrypt_flags_t +#define gnutls_pkcs12_bag_type gnutls_pkcs12_bag_type_t +#define gnutls_pkcs12_bag gnutls_pkcs12_bag_t +#define gnutls_pkcs12 gnutls_pkcs12_t +#define gnutls_certificate_credentials gnutls_certificate_credentials_t +#define gnutls_anon_server_credentials gnutls_anon_server_credentials_t +#define gnutls_anon_client_credentials gnutls_anon_client_credentials_t +#define gnutls_srp_client_credentials gnutls_srp_client_credentials_t +#define gnutls_srp_server_credentials gnutls_srp_server_credentials_t +#define gnutls_dh_params gnutls_dh_params_t +#define gnutls_rsa_params gnutls_rsa_params_t +#define gnutls_params_type gnutls_params_type_t +#define gnutls_credentials_type gnutls_credentials_type_t +#define gnutls_certificate_type gnutls_certificate_type_t +#define gnutls_datum gnutls_datum_t +#define gnutls_transport_ptr gnutls_transport_ptr_t + +/* Old SRP alerts removed in 2.1.x because the TLS-SRP RFC was + modified to use the PSK alert. */ +#define GNUTLS_A_MISSING_SRP_USERNAME GNUTLS_A_UNKNOWN_PSK_IDENTITY +#define GNUTLS_A_UNKNOWN_SRP_USERNAME GNUTLS_A_UNKNOWN_PSK_IDENTITY + +/* OpenPGP stuff renamed in 2.1.x. */ +#define gnutls_openpgp_key_fmt_t gnutls_openpgp_crt_fmt_t +#define GNUTLS_OPENPGP_KEY GNUTLS_OPENPGP_CERT +#define GNUTLS_OPENPGP_KEY_FINGERPRINT GNUTLS_OPENPGP_CERT_FINGERPRINT +#define gnutls_openpgp_send_key gnutls_openpgp_send_cert +#define gnutls_openpgp_key_status_t gnutls_openpgp_crt_status_t +#define gnutls_openpgp_key_t gnutls_openpgp_crt_t +#define gnutls_openpgp_key_init gnutls_openpgp_crt_init +#define gnutls_openpgp_key_deinit gnutls_openpgp_crt_deinit +#define gnutls_openpgp_key_import gnutls_openpgp_crt_import +#define gnutls_openpgp_key_export gnutls_openpgp_crt_export +#define gnutls_openpgp_key_get_key_usage gnutls_openpgp_crt_get_key_usage +#define gnutls_openpgp_key_get_fingerprint gnutls_openpgp_crt_get_fingerprint +#define gnutls_openpgp_key_get_pk_algorithm gnutls_openpgp_crt_get_pk_algorithm +#define gnutls_openpgp_key_get_name gnutls_openpgp_crt_get_name +#define gnutls_openpgp_key_get_version gnutls_openpgp_crt_get_version +#define gnutls_openpgp_key_get_creation_time gnutls_openpgp_crt_get_creation_time +#define gnutls_openpgp_key_get_expiration_time gnutls_openpgp_crt_get_expiration_time +#define gnutls_openpgp_key_get_id gnutls_openpgp_crt_get_id +#define gnutls_openpgp_key_check_hostname gnutls_openpgp_crt_check_hostname + +#endif /* GCOMPAT_H */ diff --git a/src/daemon/https/includes/extra.h b/src/daemon/https/includes/extra.h new file mode 100644 index 00000000..ca0cb5b4 --- /dev/null +++ b/src/daemon/https/includes/extra.h @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2002, 2003, 2004, 2005, 2007 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS-EXTRA. + * + * GNUTLS-EXTRA is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * GNUTLS-EXTRA 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 GNUTLS-EXTRA; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +/* Note the libgnutls-extra is not a standalone library. It requires + * to link also against libgnutls. + */ + +#ifndef GNUTLS_EXTRA_H +# define GNUTLS_EXTRA_H + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define LIBGNUTLS_EXTRA_VERSION LIBGNUTLS_VERSION + +/* Openpgp certificate stuff + */ + + typedef enum gnutls_openpgp_crt_fmt + { GNUTLS_OPENPGP_FMT_RAW, + GNUTLS_OPENPGP_FMT_BASE64 + } gnutls_openpgp_crt_fmt_t; + +/** + * gnutls_openpgp_recv_key_func - Callback prototype to get OpenPGP keys + * @session: a TLS session + * @keyfpr: key fingerprint + * @keyfpr_length: length of key fingerprint + * @key: output key. + * + * A callback of this type is used to retrieve OpenPGP keys. Only + * useful on the server, and will only be used if the peer send a key + * fingerprint instead of a full key. See also + * gnutls_openpgp_set_recv_key_function(). + * + */ + typedef int (*gnutls_openpgp_recv_key_func) (gnutls_session_t session, + const unsigned char *keyfpr, + unsigned int keyfpr_length, + gnutls_datum_t * key); + + void gnutls_openpgp_set_recv_key_function (gnutls_session_t session, + gnutls_openpgp_recv_key_func + func); + + int + gnutls_certificate_set_openpgp_key_file (gnutls_certificate_credentials_t + res, const char *CERTFILE, + const char *KEYFILE, gnutls_openpgp_crt_fmt_t); + int gnutls_certificate_set_openpgp_key_mem (gnutls_certificate_credentials_t + res, + const gnutls_datum_t * CERT, + const gnutls_datum_t * KEY, gnutls_openpgp_crt_fmt_t); + + int + gnutls_certificate_set_openpgp_keyring_mem + (gnutls_certificate_credentials_t c, const unsigned char *data, + size_t dlen, gnutls_openpgp_crt_fmt_t); + + int + gnutls_certificate_set_openpgp_keyring_file + (gnutls_certificate_credentials_t c, const char *file, gnutls_openpgp_crt_fmt_t); + + /* TLS/IA stuff + */ + + typedef enum + { + GNUTLS_IA_APPLICATION_PAYLOAD = 0, + GNUTLS_IA_INTERMEDIATE_PHASE_FINISHED = 1, + GNUTLS_IA_FINAL_PHASE_FINISHED = 2 + } gnutls_ia_apptype_t; + + /* TLS/IA credential + */ + + typedef int (*gnutls_ia_avp_func) (gnutls_session_t session, void *ptr, + const char *last, size_t lastlen, + char **next, size_t * nextlen); + + typedef struct gnutls_ia_server_credentials_st + *gnutls_ia_server_credentials_t; + typedef struct gnutls_ia_client_credentials_st + *gnutls_ia_client_credentials_t; + + /* Allocate and free TLS/IA credentials. */ + extern void + gnutls_ia_free_client_credentials (gnutls_ia_client_credentials_t sc); + extern int + gnutls_ia_allocate_client_credentials (gnutls_ia_client_credentials_t * + sc); + + extern void + gnutls_ia_free_server_credentials (gnutls_ia_server_credentials_t sc); + extern int + gnutls_ia_allocate_server_credentials (gnutls_ia_server_credentials_t * + sc); + + /* Client TLS/IA credential functions. */ + extern void + gnutls_ia_set_client_avp_function (gnutls_ia_client_credentials_t cred, + gnutls_ia_avp_func avp_func); + extern void + gnutls_ia_set_client_avp_ptr (gnutls_ia_client_credentials_t cred, + void *ptr); + extern void *gnutls_ia_get_client_avp_ptr (gnutls_ia_client_credentials_t + cred); + + /* Server TLS/IA credential functions. */ + extern void + gnutls_ia_set_server_avp_function (gnutls_ia_server_credentials_t cred, + gnutls_ia_avp_func avp_func); + extern void + gnutls_ia_set_server_avp_ptr (gnutls_ia_server_credentials_t cred, + void *ptr); + extern void *gnutls_ia_get_server_avp_ptr (gnutls_ia_server_credentials_t + cred); + + /* TLS/IA handshake. */ + extern int gnutls_ia_handshake_p (gnutls_session_t session); + + extern int gnutls_ia_handshake (gnutls_session_t session); + + /* TLS/IA low level interface. */ + extern int + gnutls_ia_permute_inner_secret (gnutls_session_t session, + size_t session_keys_size, + const char *session_keys); + extern int gnutls_ia_endphase_send (gnutls_session_t session, + int final_p); + + extern int gnutls_ia_verify_endphase (gnutls_session_t session, + const char *checksum); + + extern ssize_t gnutls_ia_send (gnutls_session_t session, + const char *data, size_t sizeofdata); + extern ssize_t gnutls_ia_recv (gnutls_session_t session, + char *data, size_t sizeofdata); + + /* Utility stuff. */ + extern int gnutls_ia_generate_challenge (gnutls_session_t session, + size_t buffer_size, + char *buffer); + extern void gnutls_ia_extract_inner_secret (gnutls_session_t session, + char *buffer); + + /* Define whether inner phases are wanted. */ + extern void gnutls_ia_enable (gnutls_session_t session, + int allow_skip_on_resume); + + int gnutls_global_init_extra (void); + +/* returns libgnutls-extra version (call it with a NULL argument) + */ + const char *gnutls_extra_check_version (const char *req_version); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/daemon/https/includes/gnutls.h b/src/daemon/https/includes/gnutls.h new file mode 100644 index 00000000..b5d168f7 --- /dev/null +++ b/src/daemon/https/includes/gnutls.h @@ -0,0 +1,1286 @@ +/* -*- c -*- + * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation + * + * Author: Nikos Mavroyanopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + * + */ + +/* This file contains the types and prototypes for all the + * high level functionality of gnutls main library. For the + * extra functionality (which is under the GNU GPL license) check + * the gnutls/extra.h header. The openssl compatibility layer is + * in gnutls/openssl.h. + * + * The low level cipher functionality is in libgcrypt. Check + * gcrypt.h + */ + + +#ifndef GNUTLS_H +# define GNUTLS_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define LIBGNUTLS_VERSION "2.2.3" + +#define LIBGNUTLS_VERSION_MAJOR 2 +#define LIBGNUTLS_VERSION_MINOR 2 +#define LIBGNUTLS_VERSION_PATCH 3 + +#define LIBGNUTLS_VERSION_NUMBER 0x020203 + + + +/* Get size_t. */ +#include +/* Get ssize_t. */ +#ifndef HAVE_SSIZE_T +# define HAVE_SSIZE_T +#include +#endif +/* Get time_t. */ +#include +#include + +#define GNUTLS_CIPHER_RIJNDAEL_128_CBC GNUTLS_CIPHER_AES_128_CBC +#define GNUTLS_CIPHER_RIJNDAEL_256_CBC GNUTLS_CIPHER_AES_256_CBC +#define GNUTLS_CIPHER_RIJNDAEL_CBC GNUTLS_CIPHER_AES_128_CBC +#define GNUTLS_CIPHER_ARCFOUR GNUTLS_CIPHER_ARCFOUR_128 + + typedef enum gnutls_cipher_algorithm + { + GNUTLS_CIPHER_UNKNOWN = 0, + GNUTLS_CIPHER_NULL = 1, + GNUTLS_CIPHER_ARCFOUR_128, + GNUTLS_CIPHER_3DES_CBC, + GNUTLS_CIPHER_AES_128_CBC, + GNUTLS_CIPHER_AES_256_CBC, + GNUTLS_CIPHER_ARCFOUR_40, + GNUTLS_CIPHER_CAMELLIA_128_CBC, + GNUTLS_CIPHER_CAMELLIA_256_CBC, + GNUTLS_CIPHER_RC2_40_CBC = 90, + GNUTLS_CIPHER_DES_CBC + } gnutls_cipher_algorithm_t; + + typedef enum + { + GNUTLS_KX_UNKNOWN = 0, + GNUTLS_KX_RSA = 1, + GNUTLS_KX_DHE_DSS, + GNUTLS_KX_DHE_RSA, + GNUTLS_KX_ANON_DH, + GNUTLS_KX_SRP, + GNUTLS_KX_RSA_EXPORT, + GNUTLS_KX_SRP_RSA, + GNUTLS_KX_SRP_DSS, + GNUTLS_KX_PSK, + GNUTLS_KX_DHE_PSK + } gnutls_kx_algorithm_t; + + typedef enum + { + GNUTLS_PARAMS_RSA_EXPORT = 1, + GNUTLS_PARAMS_DH + } gnutls_params_type_t; + + typedef enum + { + GNUTLS_CRD_CERTIFICATE = 1, + GNUTLS_CRD_ANON, + GNUTLS_CRD_SRP, + GNUTLS_CRD_PSK, + GNUTLS_CRD_IA + } gnutls_credentials_type_t; + +#define GNUTLS_MAC_SHA GNUTLS_MAC_SHA1 +#define GNUTLS_DIG_SHA GNUTLS_DIG_SHA1 + + typedef enum + { + GNUTLS_MAC_UNKNOWN = 0, + GNUTLS_MAC_NULL = 1, + GNUTLS_MAC_MD5, + GNUTLS_MAC_SHA1, + //GNUTLS_MAC_RMD160, + //GNUTLS_MAC_MD2, + GNUTLS_MAC_SHA256, + //GNUTLS_MAC_SHA384, + //GNUTLS_MAC_SHA512 + } gnutls_mac_algorithm_t; + + /* The enumerations here should have the same value with + gnutls_mac_algorithm_t. + */ + typedef enum + { + GNUTLS_DIG_NULL = GNUTLS_MAC_NULL, + GNUTLS_DIG_MD5 = GNUTLS_MAC_MD5, + GNUTLS_DIG_SHA1 = GNUTLS_MAC_SHA1, + GNUTLS_DIG_SHA256 = GNUTLS_MAC_SHA256, + } gnutls_digest_algorithm_t; + + /* exported for other gnutls headers. This is the maximum number of + * algorithms (ciphers, kx or macs). + */ +#define GNUTLS_MAX_ALGORITHM_NUM 16 + +#define GNUTLS_COMP_ZLIB GNUTLS_COMP_DEFLATE + typedef enum + { + GNUTLS_COMP_UNKNOWN = 0, + GNUTLS_COMP_NULL = 1, + GNUTLS_COMP_DEFLATE, + GNUTLS_COMP_LZO /* only available if gnutls-extra has + been initialized + */ + } gnutls_compression_method_t; + + typedef enum + { + GNUTLS_SERVER = 1, + GNUTLS_CLIENT + } gnutls_connection_end_t; + + typedef enum + { + GNUTLS_AL_WARNING = 1, + GNUTLS_AL_FATAL + } gnutls_alert_level_t; + + typedef enum + { + GNUTLS_A_CLOSE_NOTIFY, + GNUTLS_A_UNEXPECTED_MESSAGE = 10, + GNUTLS_A_BAD_RECORD_MAC = 20, + GNUTLS_A_DECRYPTION_FAILED, + GNUTLS_A_RECORD_OVERFLOW, + GNUTLS_A_DECOMPRESSION_FAILURE = 30, + GNUTLS_A_HANDSHAKE_FAILURE = 40, + GNUTLS_A_SSL3_NO_CERTIFICATE = 41, + GNUTLS_A_BAD_CERTIFICATE = 42, + GNUTLS_A_UNSUPPORTED_CERTIFICATE, + GNUTLS_A_CERTIFICATE_REVOKED, + GNUTLS_A_CERTIFICATE_EXPIRED, + GNUTLS_A_CERTIFICATE_UNKNOWN, + GNUTLS_A_ILLEGAL_PARAMETER, + GNUTLS_A_UNKNOWN_CA, + GNUTLS_A_ACCESS_DENIED, + GNUTLS_A_DECODE_ERROR = 50, + GNUTLS_A_DECRYPT_ERROR, + GNUTLS_A_EXPORT_RESTRICTION = 60, + GNUTLS_A_PROTOCOL_VERSION = 70, + GNUTLS_A_INSUFFICIENT_SECURITY, + GNUTLS_A_INTERNAL_ERROR = 80, + GNUTLS_A_USER_CANCELED = 90, + GNUTLS_A_NO_RENEGOTIATION = 100, + GNUTLS_A_UNSUPPORTED_EXTENSION = 110, + GNUTLS_A_CERTIFICATE_UNOBTAINABLE = 111, + GNUTLS_A_UNRECOGNIZED_NAME = 112, + GNUTLS_A_UNKNOWN_PSK_IDENTITY = 115, + GNUTLS_A_INNER_APPLICATION_FAILURE = 208, + GNUTLS_A_INNER_APPLICATION_VERIFICATION = 209 + } gnutls_alert_description_t; + + typedef enum + { GNUTLS_HANDSHAKE_HELLO_REQUEST = 0, + GNUTLS_HANDSHAKE_CLIENT_HELLO = 1, + GNUTLS_HANDSHAKE_SERVER_HELLO = 2, + GNUTLS_HANDSHAKE_CERTIFICATE_PKT = 11, + GNUTLS_HANDSHAKE_SERVER_KEY_EXCHANGE = 12, + GNUTLS_HANDSHAKE_CERTIFICATE_REQUEST = 13, + GNUTLS_HANDSHAKE_SERVER_HELLO_DONE = 14, + GNUTLS_HANDSHAKE_CERTIFICATE_VERIFY = 15, + GNUTLS_HANDSHAKE_CLIENT_KEY_EXCHANGE = 16, + GNUTLS_HANDSHAKE_FINISHED = 20, + GNUTLS_HANDSHAKE_SUPPLEMENTAL = 23 + } gnutls_handshake_description_t; + +/* Note that the status bits have different meanings + * in openpgp keys and x.509 certificate verification. + */ + typedef enum + { + GNUTLS_CERT_INVALID = 2, /* will be set if the certificate + * was not verified. + */ + GNUTLS_CERT_REVOKED = 32, /* in X.509 this will be set only if CRLs are checked + */ + + /* Those are extra information about the verification + * process. Will be set only if the certificate was + * not verified. + */ + GNUTLS_CERT_SIGNER_NOT_FOUND = 64, + GNUTLS_CERT_SIGNER_NOT_CA = 128, + GNUTLS_CERT_INSECURE_ALGORITHM = 256 + } gnutls_certificate_status_t; + + typedef enum + { + GNUTLS_CERT_IGNORE, + GNUTLS_CERT_REQUEST = 1, + GNUTLS_CERT_REQUIRE + } gnutls_certificate_request_t; + + typedef enum + { GNUTLS_OPENPGP_CERT, + GNUTLS_OPENPGP_CERT_FINGERPRINT + } gnutls_openpgp_crt_status_t; + + typedef enum + { + GNUTLS_SHUT_RDWR = 0, + GNUTLS_SHUT_WR = 1 + } gnutls_close_request_t; + +#define GNUTLS_TLS1 GNUTLS_TLS1_0 + typedef enum + { + GNUTLS_SSL3 = 1, + GNUTLS_TLS1_0, + GNUTLS_TLS1_1, + GNUTLS_TLS1_2, + GNUTLS_VERSION_UNKNOWN = 0xff + } gnutls_protocol_t; + + typedef enum + { + GNUTLS_CRT_UNKNOWN = 0, + GNUTLS_CRT_X509 = 1, + GNUTLS_CRT_OPENPGP + } gnutls_certificate_type_t; + + typedef enum + { + GNUTLS_X509_FMT_DER, + GNUTLS_X509_FMT_PEM + } gnutls_x509_crt_fmt_t; + + typedef enum + { + GNUTLS_PK_UNKNOWN = 0, + GNUTLS_PK_RSA = 1, + //GNUTLS_PK_DSA + } gnutls_pk_algorithm_t; + + const char *gnutls_pk_algorithm_get_name (gnutls_pk_algorithm_t algorithm); + +#define GNUTLS_SIGN_RSA_SHA GNUTLS_SIGN_RSA_SHA1 +#define GNUTLS_SIGN_DSA_SHA GNUTLS_SIGN_DSA_SHA1 + typedef enum + { + GNUTLS_SIGN_UNKNOWN = 0, + GNUTLS_SIGN_RSA_SHA1 = 1, + GNUTLS_SIGN_DSA_SHA1, + GNUTLS_SIGN_RSA_MD5, + GNUTLS_SIGN_RSA_MD2, + GNUTLS_SIGN_RSA_RMD160, + GNUTLS_SIGN_RSA_SHA256, + GNUTLS_SIGN_RSA_SHA384, + GNUTLS_SIGN_RSA_SHA512 + } gnutls_sign_algorithm_t; + + const char *gnutls_sign_algorithm_get_name (gnutls_sign_algorithm_t + algorithm); + +/* If you want to change this, then also change the define in + * gnutls_int.h, and recompile. + */ + typedef void * gnutls_transport_ptr_t; + + struct gnutls_session_int; + typedef struct gnutls_session_int * gnutls_session_t; + + struct gnutls_dh_params_int; + typedef struct gnutls_dh_params_int *gnutls_dh_params_t; + + struct gnutls_x509_privkey_int; /* XXX ugly. */ + typedef struct gnutls_x509_privkey_int *gnutls_rsa_params_t; /* XXX ugly. */ + + struct gnutls_priority_st; + typedef struct gnutls_priority_st *gnutls_priority_t; + + typedef struct + { + unsigned char *data; + unsigned int size; + } gnutls_datum_t; + + + typedef struct gnutls_params_st + { + gnutls_params_type_t type; + union params + { + gnutls_dh_params_t dh; + gnutls_rsa_params_t rsa_export; + } params; + int deinit; + } gnutls_params_st; + + typedef int gnutls_params_function (gnutls_session_t, gnutls_params_type_t, + gnutls_params_st *); + +/* internal functions */ + + int gnutls_init (gnutls_session_t * session, + gnutls_connection_end_t con_end); + void gnutls_deinit (gnutls_session_t session); +#define _gnutls_deinit(x) gnutls_deinit(x) + + int gnutls_bye (gnutls_session_t session, gnutls_close_request_t how); + + int gnutls_handshake (gnutls_session_t session); + int gnutls_rehandshake (gnutls_session_t session); + + gnutls_alert_description_t gnutls_alert_get (gnutls_session_t session); + int gnutls_alert_send (gnutls_session_t session, + gnutls_alert_level_t level, + gnutls_alert_description_t desc); + int gnutls_alert_send_appropriate (gnutls_session_t session, int err); + const char *gnutls_alert_get_name (gnutls_alert_description_t alert); + +/* get information on the current session */ + gnutls_cipher_algorithm_t gnutls_cipher_get (gnutls_session_t session); + gnutls_kx_algorithm_t gnutls_kx_get (gnutls_session_t session); + gnutls_mac_algorithm_t gnutls_mac_get (gnutls_session_t session); + gnutls_compression_method_t gnutls_compression_get (gnutls_session_t + session); + gnutls_certificate_type_t gnutls_certificate_type_get (gnutls_session_t + session); + + size_t gnutls_cipher_get_key_size (gnutls_cipher_algorithm_t algorithm); + size_t gnutls_mac_get_key_size (gnutls_mac_algorithm_t algorithm); + +/* the name of the specified algorithms */ + const char *gnutls_cipher_get_name (gnutls_cipher_algorithm_t algorithm); + const char *gnutls_mac_get_name (gnutls_mac_algorithm_t algorithm); + const char *gnutls_compression_get_name (gnutls_compression_method_t + algorithm); + const char *gnutls_kx_get_name (gnutls_kx_algorithm_t algorithm); + const char *gnutls_certificate_type_get_name (gnutls_certificate_type_t + type); + + gnutls_mac_algorithm_t gnutls_mac_get_id (const char* name); + gnutls_compression_method_t gnutls_compression_get_id (const char* name); + gnutls_cipher_algorithm_t gnutls_cipher_get_id (const char* name); + gnutls_kx_algorithm_t gnutls_kx_get_id (const char* name); + gnutls_protocol_t gnutls_protocol_get_id (const char* name); + gnutls_certificate_type_t gnutls_certificate_type_get_id (const char* name); + + + /* list supported algorithms */ + const gnutls_cipher_algorithm_t *gnutls_cipher_list (void); + const gnutls_mac_algorithm_t *gnutls_mac_list (void); + const gnutls_compression_method_t *gnutls_compression_list (void); + const gnutls_protocol_t *gnutls_protocol_list (void); + const gnutls_certificate_type_t *gnutls_certificate_type_list (void); + const gnutls_kx_algorithm_t *gnutls_kx_list (void); + const char *gnutls_cipher_suite_info (size_t i, + char *id, + gnutls_kx_algorithm_t *kx, + gnutls_cipher_algorithm_t *cipher, + gnutls_mac_algorithm_t *mac, + gnutls_protocol_t *version); + + /* error functions */ + int gnutls_error_is_fatal (int error); + int gnutls_error_to_alert (int err, int *level); + + void gnutls_perror (int error); + const char *gnutls_strerror (int error); + +/* Semi-internal functions. + */ + void gnutls_handshake_set_private_extensions (gnutls_session_t session, + int allow); + gnutls_handshake_description_t + gnutls_handshake_get_last_out (gnutls_session_t session); + gnutls_handshake_description_t + gnutls_handshake_get_last_in (gnutls_session_t session); + +/* Record layer functions. + */ + ssize_t gnutls_record_send (gnutls_session_t session, const void *data, + size_t sizeofdata); + ssize_t gnutls_record_recv (gnutls_session_t session, void *data, + size_t sizeofdata); +#define gnutls_read gnutls_record_recv +#define gnutls_write gnutls_record_send + + void gnutls_session_enable_compatibility_mode (gnutls_session_t session); + + void gnutls_record_disable_padding (gnutls_session_t session); + + int gnutls_record_get_direction (gnutls_session_t session); + + size_t gnutls_record_get_max_size (gnutls_session_t session); + ssize_t gnutls_record_set_max_size (gnutls_session_t session, size_t size); + + size_t gnutls_record_check_pending (gnutls_session_t session); + + int gnutls_prf (gnutls_session_t session, + size_t label_size, const char *label, + int server_random_first, + size_t extra_size, const char *extra, + size_t outsize, char *out); + + int gnutls_prf_raw (gnutls_session_t session, + size_t label_size, const char *label, + size_t seed_size, const char *seed, + size_t outsize, char *out); + +/* TLS Extensions */ + + typedef enum + { + GNUTLS_NAME_DNS = 1 + } gnutls_server_name_type_t; + + int gnutls_server_name_set (gnutls_session_t session, + gnutls_server_name_type_t type, + const void *name, size_t name_length); + + int gnutls_server_name_get (gnutls_session_t session, + void *data, size_t * data_length, + unsigned int *type, unsigned int indx); + + /* Opaque PRF Input + * http://tools.ietf.org/id/draft-rescorla-tls-opaque-prf-input-00.txt + */ + + void + gnutls_oprfi_enable_client (gnutls_session_t session, + size_t len, + unsigned char *data); + + typedef int (*gnutls_oprfi_callback_func) (gnutls_session_t session, + void *userdata, + size_t oprfi_len, + const unsigned char *in_oprfi, + unsigned char *out_oprfi); + + void + gnutls_oprfi_enable_server (gnutls_session_t session, + gnutls_oprfi_callback_func cb, + void *userdata); + + /* Supplemental data, RFC 4680. */ + typedef enum + { + GNUTLS_SUPPLEMENTAL_USER_MAPPING_DATA = 0 + } gnutls_supplemental_data_format_type_t; + + const char *gnutls_supplemental_get_name + (gnutls_supplemental_data_format_type_t type); + +/* functions to set priority of cipher suites + */ + int gnutls_cipher_set_priority (gnutls_session_t session, const int *list); + int gnutls_mac_set_priority (gnutls_session_t session, const int *list); + int gnutls_compression_set_priority (gnutls_session_t session, + const int *list); + int gnutls_kx_set_priority (gnutls_session_t session, const int *list); + int gnutls_protocol_set_priority (gnutls_session_t session, + const int *list); + int gnutls_certificate_type_set_priority (gnutls_session_t session, + const int *list); + +/* if you just want some defaults, use the following. + */ + int gnutls_priority_init( gnutls_priority_t*, const char *priority, const char** err_pos); + void gnutls_priority_deinit( gnutls_priority_t); + + int gnutls_priority_set(gnutls_session_t session, gnutls_priority_t); + int gnutls_priority_set_direct(gnutls_session_t session, const char *priority, const char** err_pos); + + /* for compatibility + */ + int gnutls_set_default_priority (gnutls_session_t session); + int gnutls_set_default_export_priority (gnutls_session_t session); + +/* Returns the name of a cipher suite */ + const char *gnutls_cipher_suite_get_name (gnutls_kx_algorithm_t + kx_algorithm, + gnutls_cipher_algorithm_t + cipher_algorithm, + gnutls_mac_algorithm_t + mac_algorithm); + +/* get the currently used protocol version */ + gnutls_protocol_t gnutls_protocol_get_version (gnutls_session_t session); + + const char *gnutls_protocol_get_name (gnutls_protocol_t version); + + +/* get/set session + */ + int gnutls_session_set_data (gnutls_session_t session, + const void *session_data, + size_t session_data_size); + int gnutls_session_get_data (gnutls_session_t session, void *session_data, + size_t * session_data_size); + int gnutls_session_get_data2 (gnutls_session_t session, + gnutls_datum_t * data); + +/* returns the session ID */ +#define GNUTLS_MAX_SESSION_ID 32 + int gnutls_session_get_id (gnutls_session_t session, void *session_id, + size_t * session_id_size); + +/* returns security values. + * Do not use them unless you know what you're doing. + */ +#define TLS_MASTER_SIZE 48 +#define TLS_RANDOM_SIZE 32 + const void *gnutls_session_get_server_random (gnutls_session_t session); + const void *gnutls_session_get_client_random (gnutls_session_t session); + const void *gnutls_session_get_master_secret (gnutls_session_t session); + +/* checks if this session is a resumed one + */ + int gnutls_session_is_resumed (gnutls_session_t session); + + typedef int (*gnutls_db_store_func) (void *, gnutls_datum_t key, + gnutls_datum_t data); + typedef int (*gnutls_db_remove_func) (void *, gnutls_datum_t key); + typedef gnutls_datum_t (*gnutls_db_retr_func) (void *, gnutls_datum_t key); + + void gnutls_db_set_cache_expiration (gnutls_session_t session, int seconds); + + void gnutls_db_remove_session (gnutls_session_t session); + void gnutls_db_set_retrieve_function (gnutls_session_t session, + gnutls_db_retr_func retr_func); + void gnutls_db_set_remove_function (gnutls_session_t session, + gnutls_db_remove_func rem_func); + void gnutls_db_set_store_function (gnutls_session_t session, + gnutls_db_store_func store_func); + void gnutls_db_set_ptr (gnutls_session_t session, void *ptr); + void *gnutls_db_get_ptr (gnutls_session_t session); + int gnutls_db_check_entry (gnutls_session_t session, + gnutls_datum_t session_entry); + + typedef int (*gnutls_handshake_post_client_hello_func)(gnutls_session_t); + void gnutls_handshake_set_post_client_hello_function(gnutls_session_t, + gnutls_handshake_post_client_hello_func); + + void gnutls_handshake_set_max_packet_length (gnutls_session_t session, + size_t max); + +/* returns libgnutls version (call it with a NULL argument) + */ + const char *gnutls_check_version (const char *req_version); + +/* Functions for setting/clearing credentials + */ + void gnutls_credentials_clear (gnutls_session_t session); + +/* cred is a structure defined by the kx algorithm + */ + int gnutls_credentials_set (gnutls_session_t session, + gnutls_credentials_type_t type, void *cred); +#define gnutls_cred_set gnutls_credentials_set + +/* Credential structures - used in gnutls_credentials_set(); */ + + struct gnutls_certificate_credentials_st; + typedef struct gnutls_certificate_credentials_st + *gnutls_certificate_credentials_t; + typedef gnutls_certificate_credentials_t + gnutls_certificate_server_credentials; + typedef gnutls_certificate_credentials_t + gnutls_certificate_client_credentials; + + typedef struct gnutls_anon_server_credentials_st + *gnutls_anon_server_credentials_t; + typedef struct gnutls_anon_client_credentials_st + *gnutls_anon_client_credentials_t; + + void gnutls_anon_free_server_credentials (gnutls_anon_server_credentials_t sc); + int gnutls_anon_allocate_server_credentials (gnutls_anon_server_credentials_t * sc); + + void gnutls_anon_set_server_dh_params (gnutls_anon_server_credentials_t res, + gnutls_dh_params_t dh_params); + + void + gnutls_anon_set_server_params_function (gnutls_anon_server_credentials_t + res, + gnutls_params_function * func); + + void gnutls_anon_free_client_credentials (gnutls_anon_client_credentials_t + sc); + int + gnutls_anon_allocate_client_credentials (gnutls_anon_client_credentials_t + * sc); + +/* CERTFILE is an x509 certificate in PEM form. + * KEYFILE is a pkcs-1 private key in PEM form (for RSA keys). + */ + void gnutls_certificate_free_credentials (gnutls_certificate_credentials_t + sc); + int + gnutls_certificate_allocate_credentials (gnutls_certificate_credentials_t + * res); + + void gnutls_certificate_free_keys (gnutls_certificate_credentials_t sc); + void gnutls_certificate_free_cas (gnutls_certificate_credentials_t sc); + void gnutls_certificate_free_ca_names (gnutls_certificate_credentials_t sc); + void gnutls_certificate_free_crls (gnutls_certificate_credentials_t sc); + + void gnutls_certificate_set_dh_params (gnutls_certificate_credentials_t res, + gnutls_dh_params_t dh_params); + void + gnutls_certificate_set_rsa_export_params (gnutls_certificate_credentials_t + res, + gnutls_rsa_params_t rsa_params); + void gnutls_certificate_set_verify_flags (gnutls_certificate_credentials_t + res, unsigned int flags); + void gnutls_certificate_set_verify_limits (gnutls_certificate_credentials_t + res, unsigned int max_bits, + unsigned int max_depth); + + int gnutls_certificate_set_x509_trust_file (gnutls_certificate_credentials_t + res, const char *CAFILE, + gnutls_x509_crt_fmt_t type); + int gnutls_certificate_set_x509_trust_mem (gnutls_certificate_credentials_t + res, const gnutls_datum_t * CA, + gnutls_x509_crt_fmt_t type); + + int gnutls_certificate_set_x509_crl_file (gnutls_certificate_credentials_t + res, const char *crlfile, + gnutls_x509_crt_fmt_t type); + int gnutls_certificate_set_x509_crl_mem (gnutls_certificate_credentials_t + res, const gnutls_datum_t * CRL, + gnutls_x509_crt_fmt_t type); + + int gnutls_certificate_set_x509_key_file (gnutls_certificate_credentials_t + res, const char *CERTFILE, + const char *KEYFILE, + gnutls_x509_crt_fmt_t type); + int gnutls_certificate_set_x509_key_mem (gnutls_certificate_credentials_t + res, const gnutls_datum_t * CERT, + const gnutls_datum_t * KEY, + gnutls_x509_crt_fmt_t type); + + void gnutls_certificate_send_x509_rdn_sequence (gnutls_session_t session, + int status); + + + extern int + gnutls_certificate_set_x509_simple_pkcs12_file + (gnutls_certificate_credentials_t res, const char *pkcs12file, + gnutls_x509_crt_fmt_t type, const char *password); + +/* New functions to allow setting already parsed X.509 stuff. + */ + struct gnutls_x509_privkey_int; + typedef struct gnutls_x509_privkey_int *gnutls_x509_privkey_t; + + struct gnutls_x509_crl_int; + typedef struct gnutls_x509_crl_int *gnutls_x509_crl_t; + + struct gnutls_x509_crt_int; + typedef struct gnutls_x509_crt_int * gnutls_x509_crt_t; + + int gnutls_certificate_set_x509_key (gnutls_certificate_credentials_t res, + gnutls_x509_crt_t * cert_list, + int cert_list_size, + gnutls_x509_privkey_t key); + int gnutls_certificate_set_x509_trust (gnutls_certificate_credentials_t res, + gnutls_x509_crt_t * ca_list, + int ca_list_size); + int gnutls_certificate_set_x509_crl (gnutls_certificate_credentials_t res, + gnutls_x509_crl_t * crl_list, + int crl_list_size); + +/* global state functions + */ + int gnutls_global_init (void); + void gnutls_global_deinit (void); + + typedef void *(*gnutls_alloc_function) (size_t); + typedef void *(*gnutls_calloc_function) (size_t, size_t); + typedef int (*gnutls_is_secure_function) (const void *); + typedef void (*gnutls_free_function) (void *); + typedef void *(*gnutls_realloc_function) (void *, size_t); + + extern void + gnutls_global_set_mem_functions (gnutls_alloc_function gt_alloc_func, + gnutls_alloc_function + gt_secure_alloc_func, + gnutls_is_secure_function + gt_is_secure_func, + gnutls_realloc_function gt_realloc_func, + gnutls_free_function gt_free_func); + +/* For use in callbacks */ + extern gnutls_alloc_function gnutls_malloc; + extern gnutls_alloc_function gnutls_secure_malloc; + extern gnutls_realloc_function gnutls_realloc; + extern gnutls_calloc_function gnutls_calloc; + extern gnutls_free_function gnutls_free; + + extern char *(*gnutls_strdup) (const char *); + + typedef void (*gnutls_log_func) (int, const char *); + void gnutls_global_set_log_function (gnutls_log_func log_func); + void gnutls_global_set_log_level (int level); + +/* Diffie Hellman parameter handling. + */ + int gnutls_dh_params_init (gnutls_dh_params_t * dh_params); + void gnutls_dh_params_deinit (gnutls_dh_params_t dh_params); + int gnutls_dh_params_import_raw (gnutls_dh_params_t dh_params, + const gnutls_datum_t * prime, + const gnutls_datum_t * generator); + int gnutls_dh_params_import_pkcs3 (gnutls_dh_params_t params, + const gnutls_datum_t * pkcs3_params, + gnutls_x509_crt_fmt_t format); + int gnutls_dh_params_generate2 (gnutls_dh_params_t params, + unsigned int bits); + int gnutls_dh_params_export_pkcs3 (gnutls_dh_params_t params, + gnutls_x509_crt_fmt_t format, + unsigned char *params_data, + size_t * params_data_size); + int gnutls_dh_params_export_raw (gnutls_dh_params_t params, + gnutls_datum_t * prime, + gnutls_datum_t * generator, + unsigned int *bits); + int gnutls_dh_params_cpy (gnutls_dh_params_t dst, gnutls_dh_params_t src); + + +/* RSA params + */ + int gnutls_rsa_params_init (gnutls_rsa_params_t * rsa_params); + void gnutls_rsa_params_deinit (gnutls_rsa_params_t rsa_params); + int gnutls_rsa_params_cpy (gnutls_rsa_params_t dst, + gnutls_rsa_params_t src); + int gnutls_rsa_params_import_raw (gnutls_rsa_params_t rsa_params, + const gnutls_datum_t * m, + const gnutls_datum_t * e, + const gnutls_datum_t * d, + const gnutls_datum_t * p, + const gnutls_datum_t * q, + const gnutls_datum_t * u); + int gnutls_rsa_params_generate2 (gnutls_rsa_params_t params, + unsigned int bits); + int gnutls_rsa_params_export_raw (gnutls_rsa_params_t params, + gnutls_datum_t * m, gnutls_datum_t * e, + gnutls_datum_t * d, gnutls_datum_t * p, + gnutls_datum_t * q, gnutls_datum_t * u, + unsigned int *bits); + int gnutls_rsa_params_export_pkcs1 (gnutls_rsa_params_t params, + gnutls_x509_crt_fmt_t format, + unsigned char *params_data, + size_t * params_data_size); + int gnutls_rsa_params_import_pkcs1 (gnutls_rsa_params_t params, + const gnutls_datum_t * pkcs1_params, + gnutls_x509_crt_fmt_t format); + +/* Session stuff + */ + typedef ssize_t (*gnutls_pull_func) (gnutls_transport_ptr_t, void *, + size_t); + typedef ssize_t (*gnutls_push_func) (gnutls_transport_ptr_t, const void *, + size_t); + void gnutls_transport_set_ptr (gnutls_session_t session, + gnutls_transport_ptr_t ptr); + void gnutls_transport_set_ptr2 (gnutls_session_t session, + gnutls_transport_ptr_t recv_ptr, + gnutls_transport_ptr_t send_ptr); + + gnutls_transport_ptr_t gnutls_transport_get_ptr (gnutls_session_t session); + void gnutls_transport_get_ptr2 (gnutls_session_t session, + gnutls_transport_ptr_t * recv_ptr, + gnutls_transport_ptr_t * send_ptr); + + void gnutls_transport_set_lowat (gnutls_session_t session, int num); + + + void gnutls_transport_set_push_function (gnutls_session_t session, + gnutls_push_func push_func); + void gnutls_transport_set_pull_function (gnutls_session_t session, + gnutls_pull_func pull_func); + + void gnutls_transport_set_errno (gnutls_session_t session, int err); + void gnutls_transport_set_global_errno (int err); + +/* session specific + */ + void gnutls_session_set_ptr (gnutls_session_t session, void *ptr); + void *gnutls_session_get_ptr (gnutls_session_t session); + + void gnutls_openpgp_send_cert (gnutls_session_t session, + gnutls_openpgp_crt_status_t status); + +/* fingerprint + * Actually this function returns the hash of the given data. + */ + int gnutls_fingerprint (gnutls_digest_algorithm_t algo, + const gnutls_datum_t * data, void *result, + size_t * result_size); + + +/* SRP + */ + + typedef struct gnutls_srp_server_credentials_st + *gnutls_srp_server_credentials_t; + typedef struct gnutls_srp_client_credentials_st + *gnutls_srp_client_credentials_t; + + void gnutls_srp_free_client_credentials (gnutls_srp_client_credentials_t + sc); + int gnutls_srp_allocate_client_credentials (gnutls_srp_client_credentials_t + * sc); + int gnutls_srp_set_client_credentials (gnutls_srp_client_credentials_t res, + const char *username, const char *password); + + void gnutls_srp_free_server_credentials (gnutls_srp_server_credentials_t + sc); + int gnutls_srp_allocate_server_credentials (gnutls_srp_server_credentials_t + * sc); + int gnutls_srp_set_server_credentials_file (gnutls_srp_server_credentials_t + res, const char *password_file, + const char *password_conf_file); + + const char *gnutls_srp_server_get_username (gnutls_session_t session); + + extern int gnutls_srp_verifier (const char *username, + const char *password, + const gnutls_datum_t * salt, + const gnutls_datum_t * generator, + const gnutls_datum_t * prime, + gnutls_datum_t * res); + +/* The static parameters defined in draft-ietf-tls-srp-05 + * Those should be used as input to gnutls_srp_verifier(). + */ + extern const gnutls_datum_t gnutls_srp_2048_group_prime; + extern const gnutls_datum_t gnutls_srp_2048_group_generator; + + extern const gnutls_datum_t gnutls_srp_1536_group_prime; + extern const gnutls_datum_t gnutls_srp_1536_group_generator; + + extern const gnutls_datum_t gnutls_srp_1024_group_prime; + extern const gnutls_datum_t gnutls_srp_1024_group_generator; + + typedef int gnutls_srp_server_credentials_function (gnutls_session_t, + const char *username, + gnutls_datum_t * salt, + gnutls_datum_t * + verifier, + gnutls_datum_t * + generator, + gnutls_datum_t * prime); + void + gnutls_srp_set_server_credentials_function + (gnutls_srp_server_credentials_t cred, + gnutls_srp_server_credentials_function * func); + + typedef int gnutls_srp_client_credentials_function (gnutls_session_t, + char **, char **); + void + gnutls_srp_set_client_credentials_function + (gnutls_srp_client_credentials_t cred, + gnutls_srp_client_credentials_function * func); + + int gnutls_srp_base64_encode (const gnutls_datum_t * data, char *result, + size_t * result_size); + int gnutls_srp_base64_encode_alloc (const gnutls_datum_t * data, + gnutls_datum_t * result); + + int gnutls_srp_base64_decode (const gnutls_datum_t * b64_data, char *result, + size_t * result_size); + int gnutls_srp_base64_decode_alloc (const gnutls_datum_t * b64_data, + gnutls_datum_t * result); + +/* PSK stuff */ + typedef struct gnutls_psk_server_credentials_st + *gnutls_psk_server_credentials_t; + typedef struct gnutls_psk_client_credentials_st + *gnutls_psk_client_credentials_t; + + typedef enum gnutls_psk_key_flags + { + GNUTLS_PSK_KEY_RAW = 0, + GNUTLS_PSK_KEY_HEX + } gnutls_psk_key_flags; + + void gnutls_psk_free_client_credentials (gnutls_psk_client_credentials_t + sc); + int gnutls_psk_allocate_client_credentials (gnutls_psk_client_credentials_t + * sc); + int gnutls_psk_set_client_credentials (gnutls_psk_client_credentials_t res, + const char *username, + const gnutls_datum_t * key, + gnutls_psk_key_flags format); + + void gnutls_psk_free_server_credentials (gnutls_psk_server_credentials_t + sc); + int gnutls_psk_allocate_server_credentials (gnutls_psk_server_credentials_t + * sc); + int gnutls_psk_set_server_credentials_file (gnutls_psk_server_credentials_t + res, const char *password_file); + + const char *gnutls_psk_server_get_username (gnutls_session_t session); + + typedef int gnutls_psk_server_credentials_function (gnutls_session_t, + const char *username, + gnutls_datum_t * key); + void + gnutls_psk_set_server_credentials_function + (gnutls_psk_server_credentials_t cred, + gnutls_psk_server_credentials_function * func); + + typedef int gnutls_psk_client_credentials_function (gnutls_session_t, + char **username, + gnutls_datum_t * key); + void + gnutls_psk_set_client_credentials_function + (gnutls_psk_client_credentials_t cred, + gnutls_psk_client_credentials_function * func); + + int gnutls_hex_encode (const gnutls_datum_t * data, char *result, + size_t * result_size); + int gnutls_hex_decode (const gnutls_datum_t * hex_data, char *result, + size_t * result_size); + + void gnutls_psk_set_server_dh_params (gnutls_psk_server_credentials_t res, + gnutls_dh_params_t dh_params); + + void gnutls_psk_set_server_params_function (gnutls_psk_server_credentials_t + res, + gnutls_params_function * func); + + typedef enum gnutls_x509_subject_alt_name_t + { + GNUTLS_SAN_DNSNAME = 1, + GNUTLS_SAN_RFC822NAME, + GNUTLS_SAN_URI, + GNUTLS_SAN_IPADDRESS, + GNUTLS_SAN_OTHERNAME, + GNUTLS_SAN_DN, + /* The following are "virtual" subject alternative name types, in + that they are represented by an otherName value and an OID. + Used by gnutls_x509_crt_get_subject_alt_othername_oid(). */ + GNUTLS_SAN_OTHERNAME_XMPP = 1000 + } gnutls_x509_subject_alt_name_t; + + struct gnutls_openpgp_crt_int; + typedef struct gnutls_openpgp_crt_int *gnutls_openpgp_crt_t; + + struct gnutls_openpgp_privkey_int; + typedef struct gnutls_openpgp_privkey_int *gnutls_openpgp_privkey_t; + + typedef struct gnutls_retr_st + { + gnutls_certificate_type_t type; + union cert + { + gnutls_x509_crt_t *x509; + gnutls_openpgp_crt_t pgp; + } cert; + unsigned int ncerts; /* one for pgp keys */ + + union key + { + gnutls_x509_privkey_t x509; + gnutls_openpgp_privkey_t pgp; + } key; + + unsigned int deinit_all; /* if non zero all keys will be deinited */ + } gnutls_retr_st; + + typedef int gnutls_certificate_client_retrieve_function (gnutls_session_t, + const + gnutls_datum_t * + req_ca_rdn, + int nreqs, + const + gnutls_pk_algorithm_t + * pk_algos, + int + pk_algos_length, + gnutls_retr_st *); + typedef int gnutls_certificate_server_retrieve_function (gnutls_session_t, + gnutls_retr_st *); + + + /* Functions that allow auth_info_t structures handling + */ + + gnutls_credentials_type_t gnutls_auth_get_type (gnutls_session_t session); + gnutls_credentials_type_t + gnutls_auth_server_get_type (gnutls_session_t session); + gnutls_credentials_type_t + gnutls_auth_client_get_type (gnutls_session_t session); + + /* DH */ + + void gnutls_dh_set_prime_bits (gnutls_session_t session, unsigned int bits); + int gnutls_dh_get_secret_bits (gnutls_session_t session); + int gnutls_dh_get_peers_public_bits (gnutls_session_t session); + int gnutls_dh_get_prime_bits (gnutls_session_t session); + + int gnutls_dh_get_group (gnutls_session_t session, gnutls_datum_t * raw_gen, + gnutls_datum_t * raw_prime); + int gnutls_dh_get_pubkey (gnutls_session_t session, + gnutls_datum_t * raw_key); + + /* RSA */ + int gnutls_rsa_export_get_pubkey (gnutls_session_t session, + gnutls_datum_t * exponent, + gnutls_datum_t * modulus); + int gnutls_rsa_export_get_modulus_bits (gnutls_session_t session); + + /* X509PKI */ + + /* External signing callback. Experimental. */ + typedef int (*gnutls_sign_func) (gnutls_session_t session, + void *userdata, + gnutls_certificate_type_t cert_type, + const gnutls_datum_t * cert, + const gnutls_datum_t * hash, + gnutls_datum_t * signature); + + void gnutls_sign_callback_set (gnutls_session_t session, + gnutls_sign_func sign_func, + void *userdata); + gnutls_sign_func + gnutls_sign_callback_get (gnutls_session_t session, + void **userdata); + + /* These are set on the credentials structure. + */ + void gnutls_certificate_client_set_retrieve_function + (gnutls_certificate_credentials_t cred, + gnutls_certificate_client_retrieve_function * func); + void gnutls_certificate_server_set_retrieve_function + (gnutls_certificate_credentials_t cred, + gnutls_certificate_server_retrieve_function * func); + + void gnutls_certificate_server_set_request (gnutls_session_t session, + gnutls_certificate_request_t + req); + + /* get data from the session + */ + const gnutls_datum_t *gnutls_certificate_get_peers (gnutls_session_t + session, + unsigned int + *list_size); + const gnutls_datum_t *gnutls_certificate_get_ours (gnutls_session_t + session); + + time_t gnutls_certificate_activation_time_peers (gnutls_session_t session); + time_t gnutls_certificate_expiration_time_peers (gnutls_session_t session); + + int gnutls_certificate_client_get_request_status (gnutls_session_t session); + int gnutls_certificate_verify_peers2 (gnutls_session_t session, + unsigned int *status); + + /* this is obsolete (?). */ + int gnutls_certificate_verify_peers (gnutls_session_t session); + + int gnutls_pem_base64_encode (const char *msg, const gnutls_datum_t * data, + char *result, size_t * result_size); + int gnutls_pem_base64_decode (const char *header, + const gnutls_datum_t * b64_data, + unsigned char *result, size_t * result_size); + + int gnutls_pem_base64_encode_alloc (const char *msg, + const gnutls_datum_t * data, + gnutls_datum_t * result); + int gnutls_pem_base64_decode_alloc (const char *header, + const gnutls_datum_t * b64_data, + gnutls_datum_t * result); + + int gnutls_global_init (void); + + /* key_usage will be an OR of the following values: + */ + + /* when the key is to be used for signing: */ +#define GNUTLS_KEY_DIGITAL_SIGNATURE 128 +#define GNUTLS_KEY_NON_REPUDIATION 64 + /* when the key is to be used for encryption: */ +#define GNUTLS_KEY_KEY_ENCIPHERMENT 32 +#define GNUTLS_KEY_DATA_ENCIPHERMENT 16 +#define GNUTLS_KEY_KEY_AGREEMENT 8 +#define GNUTLS_KEY_KEY_CERT_SIGN 4 +#define GNUTLS_KEY_CRL_SIGN 2 +#define GNUTLS_KEY_ENCIPHER_ONLY 1 +#define GNUTLS_KEY_DECIPHER_ONLY 32768 + + void + gnutls_certificate_set_params_function (gnutls_certificate_credentials_t res, + gnutls_params_function * func); + void gnutls_anon_set_params_function (gnutls_anon_server_credentials_t res, + gnutls_params_function * func); + void gnutls_psk_set_params_function (gnutls_psk_server_credentials_t res, + gnutls_params_function * func); + + /* Gnutls error codes. The mapping to a TLS alert is also shown in + * comments. + */ + +#define GNUTLS_E_SUCCESS 0 +#define GNUTLS_E_UNKNOWN_COMPRESSION_ALGORITHM -3 +#define GNUTLS_E_UNKNOWN_CIPHER_TYPE -6 +#define GNUTLS_E_LARGE_PACKET -7 +#define GNUTLS_E_UNSUPPORTED_VERSION_PACKET -8 /* GNUTLS_A_PROTOCOL_VERSION */ +#define GNUTLS_E_UNEXPECTED_PACKET_LENGTH -9 /* GNUTLS_A_RECORD_OVERFLOW */ +#define GNUTLS_E_INVALID_SESSION -10 +#define GNUTLS_E_FATAL_ALERT_RECEIVED -12 +#define GNUTLS_E_UNEXPECTED_PACKET -15 /* GNUTLS_A_UNEXPECTED_MESSAGE */ +#define GNUTLS_E_WARNING_ALERT_RECEIVED -16 +#define GNUTLS_E_ERROR_IN_FINISHED_PACKET -18 +#define GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET -19 +#define GNUTLS_E_UNKNOWN_CIPHER_SUITE -21 /* GNUTLS_A_HANDSHAKE_FAILURE */ +#define GNUTLS_E_UNWANTED_ALGORITHM -22 +#define GNUTLS_E_MPI_SCAN_FAILED -23 +#define GNUTLS_E_DECRYPTION_FAILED -24 /* GNUTLS_A_DECRYPTION_FAILED, GNUTLS_A_BAD_RECORD_MAC */ +#define GNUTLS_E_MEMORY_ERROR -25 +#define GNUTLS_E_DECOMPRESSION_FAILED -26 /* GNUTLS_A_DECOMPRESSION_FAILURE */ +#define GNUTLS_E_COMPRESSION_FAILED -27 +#define GNUTLS_E_AGAIN -28 +#define GNUTLS_E_EXPIRED -29 +#define GNUTLS_E_DB_ERROR -30 +#define GNUTLS_E_SRP_PWD_ERROR -31 +#define GNUTLS_E_INSUFFICIENT_CREDENTIALS -32 +#define GNUTLS_E_INSUFICIENT_CREDENTIALS GNUTLS_E_INSUFFICIENT_CREDENTIALS /* for backwards compatibility only */ +#define GNUTLS_E_INSUFFICIENT_CRED GNUTLS_E_INSUFFICIENT_CREDENTIALS +#define GNUTLS_E_INSUFICIENT_CRED GNUTLS_E_INSUFFICIENT_CREDENTIALS /* for backwards compatibility only */ + +#define GNUTLS_E_HASH_FAILED -33 +#define GNUTLS_E_BASE64_DECODING_ERROR -34 + +#define GNUTLS_E_MPI_PRINT_FAILED -35 +#define GNUTLS_E_REHANDSHAKE -37 /* GNUTLS_A_NO_RENEGOTIATION */ +#define GNUTLS_E_GOT_APPLICATION_DATA -38 +#define GNUTLS_E_RECORD_LIMIT_REACHED -39 +#define GNUTLS_E_ENCRYPTION_FAILED -40 + +#define GNUTLS_E_PK_ENCRYPTION_FAILED -44 +#define GNUTLS_E_PK_DECRYPTION_FAILED -45 +#define GNUTLS_E_PK_SIGN_FAILED -46 +#define GNUTLS_E_X509_UNSUPPORTED_CRITICAL_EXTENSION -47 +#define GNUTLS_E_KEY_USAGE_VIOLATION -48 +#define GNUTLS_E_NO_CERTIFICATE_FOUND -49 /* GNUTLS_A_BAD_CERTIFICATE */ +#define GNUTLS_E_INVALID_REQUEST -50 +#define GNUTLS_E_SHORT_MEMORY_BUFFER -51 +#define GNUTLS_E_INTERRUPTED -52 +#define GNUTLS_E_PUSH_ERROR -53 +#define GNUTLS_E_PULL_ERROR -54 +#define GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER -55 /* GNUTLS_A_ILLEGAL_PARAMETER */ +#define GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE -56 +#define GNUTLS_E_PKCS1_WRONG_PAD -57 +#define GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION -58 +#define GNUTLS_E_INTERNAL_ERROR -59 +#define GNUTLS_E_DH_PRIME_UNACCEPTABLE -63 +#define GNUTLS_E_FILE_ERROR -64 +#define GNUTLS_E_TOO_MANY_EMPTY_PACKETS -78 +#define GNUTLS_E_UNKNOWN_PK_ALGORITHM -80 + + + /* returned if libextra functionality was requested but + * gnutls_global_init_extra() was not called. + */ +#define GNUTLS_E_INIT_LIBEXTRA -82 +#define GNUTLS_E_LIBRARY_VERSION_MISMATCH -83 + + + /* returned if you need to generate temporary RSA + * parameters. These are needed for export cipher suites. + */ +#define GNUTLS_E_NO_TEMPORARY_RSA_PARAMS -84 + +#define GNUTLS_E_LZO_INIT_FAILED -85 +#define GNUTLS_E_NO_COMPRESSION_ALGORITHMS -86 +#define GNUTLS_E_NO_CIPHER_SUITES -87 + +#define GNUTLS_E_OPENPGP_GETKEY_FAILED -88 +#define GNUTLS_E_PK_SIG_VERIFY_FAILED -89 + +#define GNUTLS_E_ILLEGAL_SRP_USERNAME -90 +#define GNUTLS_E_SRP_PWD_PARSING_ERROR -91 +#define GNUTLS_E_NO_TEMPORARY_DH_PARAMS -93 + + /* For certificate and key stuff + */ +#define GNUTLS_E_ASN1_ELEMENT_NOT_FOUND -67 +#define GNUTLS_E_ASN1_IDENTIFIER_NOT_FOUND -68 +#define GNUTLS_E_ASN1_DER_ERROR -69 +#define GNUTLS_E_ASN1_VALUE_NOT_FOUND -70 +#define GNUTLS_E_ASN1_GENERIC_ERROR -71 +#define GNUTLS_E_ASN1_VALUE_NOT_VALID -72 +#define GNUTLS_E_ASN1_TAG_ERROR -73 +#define GNUTLS_E_ASN1_TAG_IMPLICIT -74 +#define GNUTLS_E_ASN1_TYPE_ANY_ERROR -75 +#define GNUTLS_E_ASN1_SYNTAX_ERROR -76 +#define GNUTLS_E_ASN1_DER_OVERFLOW -77 +#define GNUTLS_E_OPENPGP_UID_REVOKED -79 +#define GNUTLS_E_CERTIFICATE_ERROR -43 +#define GNUTLS_E_X509_CERTIFICATE_ERROR GNUTLS_E_CERTIFICATE_ERROR +#define GNUTLS_E_CERTIFICATE_KEY_MISMATCH -60 +#define GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE -61 /* GNUTLS_A_UNSUPPORTED_CERTIFICATE */ +#define GNUTLS_E_X509_UNKNOWN_SAN -62 +#define GNUTLS_E_OPENPGP_FINGERPRINT_UNSUPPORTED -94 +#define GNUTLS_E_X509_UNSUPPORTED_ATTRIBUTE -95 +#define GNUTLS_E_UNKNOWN_HASH_ALGORITHM -96 +#define GNUTLS_E_UNKNOWN_PKCS_CONTENT_TYPE -97 +#define GNUTLS_E_UNKNOWN_PKCS_BAG_TYPE -98 +#define GNUTLS_E_INVALID_PASSWORD -99 +#define GNUTLS_E_MAC_VERIFY_FAILED -100 /* for PKCS #12 MAC */ +#define GNUTLS_E_CONSTRAINT_ERROR -101 + +#define GNUTLS_E_WARNING_IA_IPHF_RECEIVED -102 +#define GNUTLS_E_WARNING_IA_FPHF_RECEIVED -103 + +#define GNUTLS_E_IA_VERIFY_FAILED -104 + +#define GNUTLS_E_UNKNOWN_ALGORITHM -105 + +#define GNUTLS_E_BASE64_ENCODING_ERROR -201 +#define GNUTLS_E_INCOMPATIBLE_GCRYPT_LIBRARY -202 /* obsolete */ +#define GNUTLS_E_INCOMPATIBLE_CRYPTO_LIBRARY -202 +#define GNUTLS_E_INCOMPATIBLE_LIBTASN1_LIBRARY -203 + +#define GNUTLS_E_OPENPGP_KEYRING_ERROR -204 +#define GNUTLS_E_X509_UNSUPPORTED_OID -205 + +#define GNUTLS_E_RANDOM_FAILED -206 +#define GNUTLS_E_BASE64_UNEXPECTED_HEADER_ERROR -207 + + +#define GNUTLS_E_UNIMPLEMENTED_FEATURE -1250 + +#define GNUTLS_E_APPLICATION_ERROR_MAX -65000 +#define GNUTLS_E_APPLICATION_ERROR_MIN -65500 + +#ifdef __cplusplus +} +#endif + +#endif /* GNUTLS_H */ diff --git a/src/daemon/https/includes/gnutlsxx.h b/src/daemon/https/includes/gnutlsxx.h new file mode 100644 index 00000000..3e98da12 --- /dev/null +++ b/src/daemon/https/includes/gnutlsxx.h @@ -0,0 +1,393 @@ +#ifndef GNUTLSXX_H +# define GNUTLSXX_H + +#include +#include +#include + +namespace gnutls { + +class exception: public std::exception +{ + public: + exception( int x); + const char* what() const throw(); + int get_code(); + protected: + int retcode; +}; + +class dh_params +{ + public: + dh_params(); + ~dh_params(); + void import_raw( const gnutls_datum_t & prime, + const gnutls_datum_t & generator); + void import_pkcs3( const gnutls_datum_t & pkcs3_params, + gnutls_x509_crt_fmt_t format); + void generate( unsigned int bits); + + void export_pkcs3( gnutls_x509_crt_fmt_t format, unsigned char *params_data, size_t * params_data_size); + void export_raw( gnutls_datum_t& prime, gnutls_datum_t &generator); + + gnutls_dh_params_t get_params_t() const; + dh_params & operator=(const dh_params& src); + protected: + gnutls_dh_params_t params; +}; + + +class rsa_params +{ + public: + rsa_params(); + ~rsa_params(); + void import_raw( const gnutls_datum_t & m, + const gnutls_datum_t & e, + const gnutls_datum_t & d, + const gnutls_datum_t & p, + const gnutls_datum_t & q, + const gnutls_datum_t & u); + void import_pkcs1( const gnutls_datum_t & pkcs1_params, + gnutls_x509_crt_fmt_t format); + void generate( unsigned int bits); + + void export_pkcs1( gnutls_x509_crt_fmt_t format, unsigned char *params_data, size_t * params_data_size); + void export_raw( gnutls_datum_t & m, gnutls_datum_t & e, + gnutls_datum_t & d, gnutls_datum_t & p, + gnutls_datum_t & q, gnutls_datum_t & u); + gnutls_rsa_params_t get_params_t() const; + rsa_params & operator=(const rsa_params& src); + + protected: + gnutls_rsa_params_t params; +}; + +class session +{ + protected: + gnutls_session_t s; + public: + session( gnutls_connection_end_t); + session( session& s); + virtual ~session(); + + int bye( gnutls_close_request_t how); + int handshake (); + + gnutls_alert_description_t get_alert() const; + + int send_alert ( gnutls_alert_level_t level, + gnutls_alert_description_t desc); + int send_appropriate_alert (int err); + + gnutls_cipher_algorithm_t get_cipher() const; + gnutls_kx_algorithm_t get_kx () const; + gnutls_mac_algorithm_t get_mac () const; + gnutls_compression_method_t get_compression () const; + gnutls_certificate_type_t get_certificate_type() const; + + // for the handshake + void set_private_extensions ( bool allow); + + gnutls_handshake_description_t get_handshake_last_out() const; + gnutls_handshake_description_t get_handshake_last_in() const; + + ssize_t send (const void *data, size_t sizeofdata); + ssize_t recv (void *data, size_t sizeofdata); + + bool get_record_direction() const; + + // maximum packet size + size_t get_max_size() const; + void set_max_size(size_t size); + + size_t check_pending() const; + + void prf (size_t label_size, const char *label, + int server_random_first, + size_t extra_size, const char *extra, + size_t outsize, char *out); + + void prf_raw ( size_t label_size, const char *label, + size_t seed_size, const char *seed, + size_t outsize, char *out); + + void set_cipher_priority (const int *list); + void set_mac_priority (const int *list); + void set_compression_priority (const int *list); + void set_kx_priority (const int *list); + void set_protocol_priority (const int *list); + void set_certificate_type_priority (const int *list); + +/* if you just want some defaults, use the following. + */ + void set_priority (const char* prio, const char** err_pos); + void set_priority (gnutls_priority_t p); + + gnutls_protocol_t get_protocol_version() const; + + // for resuming sessions + void set_data ( const void *session_data, + size_t session_data_size); + void get_data (void *session_data, + size_t * session_data_size) const; + void get_data(gnutls_session_t session, + gnutls_datum_t & data) const; + void get_id ( void *session_id, + size_t * session_id_size) const; + + bool is_resumed () const; + + void set_max_handshake_packet_length ( size_t max); + + void clear_credentials(); + void set_credentials( class credentials & cred); + + void set_transport_ptr( gnutls_transport_ptr_t ptr); + void set_transport_ptr( gnutls_transport_ptr_t recv_ptr, gnutls_transport_ptr_t send_ptr); + gnutls_transport_ptr_t get_transport_ptr() const; + void get_transport_ptr(gnutls_transport_ptr_t & recv_ptr, + gnutls_transport_ptr_t & send_ptr) const; + + void set_transport_lowat (size_t num); + void set_transport_push_function( gnutls_push_func push_func); + void set_transport_pull_function( gnutls_pull_func pull_func); + + void set_user_ptr( void* ptr); + void *get_user_ptr() const; + + void send_openpgp_cert( gnutls_openpgp_crt_status_t status); + + gnutls_credentials_type_t get_auth_type() const; + gnutls_credentials_type_t get_server_auth_type() const; + gnutls_credentials_type_t get_client_auth_type() const; + + // informational stuff + void set_dh_prime_bits( unsigned int bits); + unsigned int get_dh_secret_bits() const; + unsigned int get_dh_peers_public_bits() const; + unsigned int get_dh_prime_bits() const; + void get_dh_group( gnutls_datum_t & gen, gnutls_datum_t & prime) const; + void get_dh_pubkey( gnutls_datum_t & raw_key) const; + void get_rsa_export_pubkey( gnutls_datum_t& exponent, gnutls_datum_t& modulus) const; + unsigned int get_rsa_export_modulus_bits() const; + + void get_our_certificate(gnutls_datum_t & cert) const; + bool get_peers_certificate(std::vector &out_certs) const; + bool get_peers_certificate(const gnutls_datum_t** certs, unsigned int *certs_size) const; + + time_t get_peers_certificate_activation_time() const; + time_t get_peers_certificate_expiration_time() const; + void verify_peers_certificate( unsigned int& status) const; + +}; + +// interface for databases +class DB +{ + public: + virtual ~DB()=0; + virtual bool store( const gnutls_datum_t& key, const gnutls_datum_t& data)=0; + virtual bool retrieve( const gnutls_datum_t& key, gnutls_datum_t& data)=0; + virtual bool remove( const gnutls_datum_t& key)=0; +}; + +class server_session: public session +{ + public: + server_session(); + void db_remove() const; + + void set_db_cache_expiration (unsigned int seconds); + void set_db( const DB& db); + + // returns true if session is expired + bool db_check_entry ( gnutls_datum_t &session_data) const; + + // server side only + const char *get_srp_username() const; + const char *get_psk_username() const; + + void get_server_name (void *data, size_t * data_length, + unsigned int *type, unsigned int indx) const; + + int rehandshake(); + void set_certificate_request( gnutls_certificate_request_t); +}; + +class client_session: public session +{ + public: + client_session(); + void set_server_name (gnutls_server_name_type_t type, + const void *name, size_t name_length); + + bool get_request_status(); +}; + + +class credentials +{ + public: + credentials(gnutls_credentials_type_t t); +#if defined(__APPLE__) || defined(__MACOS__) + /* FIXME: This #if is due to a compile bug in Mac OS X. Give + it some time and then remove this cruft. See also + lib/gnutlsxx.cpp. */ + credentials( credentials& c) { + type = c.type; + set_ptr( c.ptr()); + } +#else + credentials( credentials& c); +#endif + virtual ~credentials() { } + gnutls_credentials_type_t get_type() const; + protected: + friend class session; + virtual void* ptr() const=0; + virtual void set_ptr(void* ptr)=0; + gnutls_credentials_type_t type; +}; + +class certificate_credentials: public credentials +{ + public: + ~certificate_credentials(); + certificate_credentials(); + + void free_keys (); + void free_cas (); + void free_ca_names (); + void free_crls (); + + void set_dh_params ( const dh_params ¶ms); + void set_rsa_export_params ( const rsa_params& params); + void set_verify_flags ( unsigned int flags); + void set_verify_limits ( unsigned int max_bits, unsigned int max_depth); + + void set_x509_trust_file(const char *cafile, gnutls_x509_crt_fmt_t type); + void set_x509_trust(const gnutls_datum_t & CA, gnutls_x509_crt_fmt_t type); + // FIXME: use classes instead of gnutls_x509_crt_t + void set_x509_trust ( gnutls_x509_crt_t * ca_list, int ca_list_size); + + void set_x509_crl_file( const char *crlfile, gnutls_x509_crt_fmt_t type); + void set_x509_crl(const gnutls_datum_t & CRL, gnutls_x509_crt_fmt_t type); + void set_x509_crl ( gnutls_x509_crl_t * crl_list, int crl_list_size); + + void set_x509_key_file(const char *certfile, const char *KEYFILE, gnutls_x509_crt_fmt_t type); + void set_x509_key(const gnutls_datum_t & CERT, const gnutls_datum_t & KEY, gnutls_x509_crt_fmt_t type); + // FIXME: use classes + void set_x509_key ( gnutls_x509_crt_t * cert_list, int cert_list_size, + gnutls_x509_privkey_t key); + + + void set_simple_pkcs12_file( const char *pkcs12file, + gnutls_x509_crt_fmt_t type, const char *password); + + protected: + void* ptr() const; + void set_ptr(void* p); + gnutls_certificate_credentials_t cred; +}; + +class certificate_server_credentials: public certificate_credentials +{ + certificate_server_credentials() { } + public: + void set_retrieve_function( gnutls_certificate_server_retrieve_function* func); + void set_params_function( gnutls_params_function* func); +}; + +class certificate_client_credentials: public certificate_credentials +{ + public: + certificate_client_credentials() { } + void set_retrieve_function( gnutls_certificate_client_retrieve_function* func); +}; + + + + +class anon_server_credentials: public credentials +{ + public: + anon_server_credentials(); + ~anon_server_credentials(); + void set_dh_params ( const dh_params ¶ms); + void set_params_function ( gnutls_params_function * func); + protected: + gnutls_anon_server_credentials_t cred; +}; + +class anon_client_credentials: public credentials +{ + public: + anon_client_credentials(); + ~anon_client_credentials(); + protected: + gnutls_anon_client_credentials_t cred; +}; + + +class srp_server_credentials: public credentials +{ + public: + srp_server_credentials(); + ~srp_server_credentials(); + void set_credentials_file (const char *password_file, const char *password_conf_file); + void set_credentials_function( gnutls_srp_server_credentials_function *func); + protected: + void* ptr() const; + void set_ptr(void* p); + gnutls_srp_server_credentials_t cred; +}; + +class srp_client_credentials: public credentials +{ + public: + srp_client_credentials(); + ~srp_client_credentials(); + void set_credentials (const char *username, const char *password); + void set_credentials_function( gnutls_srp_client_credentials_function* func); + protected: + void* ptr() const; + void set_ptr(void* p); + gnutls_srp_client_credentials_t cred; +}; + + +class psk_server_credentials: public credentials +{ + public: + psk_server_credentials(); + ~psk_server_credentials(); + void set_credentials_file(const char* password_file); + void set_credentials_function( gnutls_psk_server_credentials_function* func); + void set_dh_params ( const dh_params ¶ms); + void set_params_function (gnutls_params_function * func); + protected: + void* ptr() const; + void set_ptr(void* p); + gnutls_psk_server_credentials_t cred; +}; + +class psk_client_credentials: public credentials +{ + public: + psk_client_credentials(); + ~psk_client_credentials(); + void set_credentials (const char *username, const gnutls_datum_t& key, gnutls_psk_key_flags flags); + void set_credentials_function( gnutls_psk_client_credentials_function* func); + protected: + void* ptr() const; + void set_ptr(void* p); + gnutls_psk_client_credentials_t cred; +}; + + +}; /* namespace */ + +#endif /* GNUTLSXX_H */ diff --git a/src/daemon/https/lgl/Makefile.am b/src/daemon/https/lgl/Makefile.am new file mode 100644 index 00000000..1edb75ae --- /dev/null +++ b/src/daemon/https/lgl/Makefile.am @@ -0,0 +1,37 @@ +SUBDIRS = . + +AM_CPPFLAGS = -std=c99 \ +-I$(GCRYPT_CPPFLAGS) + +# gc-gnulib.c + +noinst_LTLIBRARIES = liblgl.la + +liblgl_la_LDFLAGS = -lgcrypt +# liblgl_la_LIBADD = ./gc-libgcrypt.lo + +liblgl_la_SOURCES = \ +asnprintf.c \ +sha1.c \ +gc-libgcrypt.c \ +time_r.c \ +rijndael-api-fst.c \ +gc-pbkdf2-sha1.c \ +read-file.c \ +rijndael-alg-fst.c \ +hmac-md5.c \ +hmac-sha1.c \ +realloc.c \ +memmem.c \ +memmove.c \ +memxor.c \ +printf-args.c \ +strverscmp.c \ +snprintf.c \ +asprintf.c \ +vasprintf.c \ +vasnprintf.c \ +md5.c \ +printf-parse.c \ +des.c + \ No newline at end of file diff --git a/src/daemon/https/lgl/asnprintf.c b/src/daemon/https/lgl/asnprintf.c new file mode 100644 index 00000000..a9114977 --- /dev/null +++ b/src/daemon/https/lgl/asnprintf.c @@ -0,0 +1,35 @@ +/* Formatted output to strings. + Copyright (C) 1999, 2002, 2006 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1, or (at your option) + any later version. + + This program 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#include + +/* Specification. */ +#include "vasnprintf.h" + +#include + +char * +asnprintf (char *resultbuf, size_t * lengthp, const char *format, ...) +{ + va_list args; + char *result; + + va_start (args, format); + result = vasnprintf (resultbuf, lengthp, format, args); + va_end (args); + return result; +} diff --git a/src/daemon/https/lgl/asprintf.c b/src/daemon/https/lgl/asprintf.c new file mode 100644 index 00000000..2df1d4b0 --- /dev/null +++ b/src/daemon/https/lgl/asprintf.c @@ -0,0 +1,39 @@ +/* Formatted output to strings. + Copyright (C) 1999, 2002, 2006-2007 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1, or (at your option) + any later version. + + This program 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#include + +/* Specification. */ +#ifdef IN_LIBASPRINTF +# include "vasprintf.h" +#else +# include +#endif + +#include + +int +asprintf (char **resultp, const char *format, ...) +{ + va_list args; + int result; + + va_start (args, format); + result = vasprintf (resultp, format, args); + va_end (args); + return result; +} diff --git a/src/daemon/https/lgl/des.c b/src/daemon/https/lgl/des.c new file mode 100644 index 00000000..fd70bc7e --- /dev/null +++ b/src/daemon/https/lgl/des.c @@ -0,0 +1,662 @@ +/* des.c --- DES and Triple-DES encryption/decryption Algorithm + * Copyright (C) 1998, 1999, 2001, 2002, 2003, 2004, 2005, 2006, 2007 + * Free Software Foundation, Inc. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2.1, or (at your + * option) any later version. + * + * This file 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 Lesser General Public License + * along with this file; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +/* Adapted for gnulib by Simon Josefsson, based on Libgcrypt. */ + +/* + * For a description of triple encryption, see: + * Bruce Schneier: Applied Cryptography. Second Edition. + * John Wiley & Sons, 1996. ISBN 0-471-12845-7. Pages 358 ff. + * This implementation is according to the definition of DES in FIPS + * PUB 46-2 from December 1993. + * + * Written by Michael Roth , September 1998 + */ + +/* + * U S A G E + * =========== + * + * For DES or Triple-DES encryption/decryption you must initialize a proper + * encryption context with a key. + * + * A DES key is 64bit wide but only 56bits of the key are used. The remaining + * bits are parity bits and they will _not_ checked in this implementation, but + * simply ignored. + * + * For Triple-DES you could use either two 64bit keys or three 64bit keys. + * The parity bits will _not_ checked, too. + * + * After initializing a context with a key you could use this context to + * encrypt or decrypt data in 64bit blocks in Electronic Codebook Mode. + * + * DES Example + * ----------- + * unsigned char key[8]; + * unsigned char plaintext[8]; + * unsigned char ciphertext[8]; + * unsigned char recoverd[8]; + * gl_des_ctx context; + * + * // Fill 'key' and 'plaintext' with some data + * .... + * + * // Set up the DES encryption context + * gl_des_setkey(&context, key); + * + * // Encrypt the plaintext + * des_ecb_encrypt(&context, plaintext, ciphertext); + * + * // To recover the orginal plaintext from ciphertext use: + * des_ecb_decrypt(&context, ciphertext, recoverd); + * + * + * Triple-DES Example + * ------------------ + * unsigned char key1[8]; + * unsigned char key2[8]; + * unsigned char key3[8]; + * unsigned char plaintext[8]; + * unsigned char ciphertext[8]; + * unsigned char recoverd[8]; + * gl_3des_ctx context; + * + * // If you would like to use two 64bit keys, fill 'key1' and'key2' + * // then setup the encryption context: + * gl_3des_set2keys(&context, key1, key2); + * + * // To use three 64bit keys with Triple-DES use: + * gl_3des_set3keys(&context, key1, key2, key3); + * + * // Encrypting plaintext with Triple-DES + * gl_3des_ecb_encrypt(&context, plaintext, ciphertext); + * + * // Decrypting ciphertext to recover the plaintext with Triple-DES + * gl_3des_ecb_decrypt(&context, ciphertext, recoverd); + */ + + +#include + +#include "des.h" + +#include +#include /* memcpy, memcmp */ + +/* + * The s-box values are permuted according to the 'primitive function P' + * and are rotated one bit to the left. + */ +static const uint32_t sbox1[64] = { + 0x01010400, 0x00000000, 0x00010000, 0x01010404, 0x01010004, 0x00010404, + 0x00000004, 0x00010000, 0x00000400, 0x01010400, 0x01010404, 0x00000400, + 0x01000404, 0x01010004, 0x01000000, 0x00000004, 0x00000404, 0x01000400, + 0x01000400, 0x00010400, 0x00010400, 0x01010000, 0x01010000, 0x01000404, + 0x00010004, 0x01000004, 0x01000004, 0x00010004, 0x00000000, 0x00000404, + 0x00010404, 0x01000000, 0x00010000, 0x01010404, 0x00000004, 0x01010000, + 0x01010400, 0x01000000, 0x01000000, 0x00000400, 0x01010004, 0x00010000, + 0x00010400, 0x01000004, 0x00000400, 0x00000004, 0x01000404, 0x00010404, + 0x01010404, 0x00010004, 0x01010000, 0x01000404, 0x01000004, 0x00000404, + 0x00010404, 0x01010400, 0x00000404, 0x01000400, 0x01000400, 0x00000000, + 0x00010004, 0x00010400, 0x00000000, 0x01010004 +}; + +static const uint32_t sbox2[64] = { + 0x80108020, 0x80008000, 0x00008000, 0x00108020, 0x00100000, 0x00000020, + 0x80100020, 0x80008020, 0x80000020, 0x80108020, 0x80108000, 0x80000000, + 0x80008000, 0x00100000, 0x00000020, 0x80100020, 0x00108000, 0x00100020, + 0x80008020, 0x00000000, 0x80000000, 0x00008000, 0x00108020, 0x80100000, + 0x00100020, 0x80000020, 0x00000000, 0x00108000, 0x00008020, 0x80108000, + 0x80100000, 0x00008020, 0x00000000, 0x00108020, 0x80100020, 0x00100000, + 0x80008020, 0x80100000, 0x80108000, 0x00008000, 0x80100000, 0x80008000, + 0x00000020, 0x80108020, 0x00108020, 0x00000020, 0x00008000, 0x80000000, + 0x00008020, 0x80108000, 0x00100000, 0x80000020, 0x00100020, 0x80008020, + 0x80000020, 0x00100020, 0x00108000, 0x00000000, 0x80008000, 0x00008020, + 0x80000000, 0x80100020, 0x80108020, 0x00108000 +}; + +static const uint32_t sbox3[64] = { + 0x00000208, 0x08020200, 0x00000000, 0x08020008, 0x08000200, 0x00000000, + 0x00020208, 0x08000200, 0x00020008, 0x08000008, 0x08000008, 0x00020000, + 0x08020208, 0x00020008, 0x08020000, 0x00000208, 0x08000000, 0x00000008, + 0x08020200, 0x00000200, 0x00020200, 0x08020000, 0x08020008, 0x00020208, + 0x08000208, 0x00020200, 0x00020000, 0x08000208, 0x00000008, 0x08020208, + 0x00000200, 0x08000000, 0x08020200, 0x08000000, 0x00020008, 0x00000208, + 0x00020000, 0x08020200, 0x08000200, 0x00000000, 0x00000200, 0x00020008, + 0x08020208, 0x08000200, 0x08000008, 0x00000200, 0x00000000, 0x08020008, + 0x08000208, 0x00020000, 0x08000000, 0x08020208, 0x00000008, 0x00020208, + 0x00020200, 0x08000008, 0x08020000, 0x08000208, 0x00000208, 0x08020000, + 0x00020208, 0x00000008, 0x08020008, 0x00020200 +}; + +static const uint32_t sbox4[64] = { + 0x00802001, 0x00002081, 0x00002081, 0x00000080, 0x00802080, 0x00800081, + 0x00800001, 0x00002001, 0x00000000, 0x00802000, 0x00802000, 0x00802081, + 0x00000081, 0x00000000, 0x00800080, 0x00800001, 0x00000001, 0x00002000, + 0x00800000, 0x00802001, 0x00000080, 0x00800000, 0x00002001, 0x00002080, + 0x00800081, 0x00000001, 0x00002080, 0x00800080, 0x00002000, 0x00802080, + 0x00802081, 0x00000081, 0x00800080, 0x00800001, 0x00802000, 0x00802081, + 0x00000081, 0x00000000, 0x00000000, 0x00802000, 0x00002080, 0x00800080, + 0x00800081, 0x00000001, 0x00802001, 0x00002081, 0x00002081, 0x00000080, + 0x00802081, 0x00000081, 0x00000001, 0x00002000, 0x00800001, 0x00002001, + 0x00802080, 0x00800081, 0x00002001, 0x00002080, 0x00800000, 0x00802001, + 0x00000080, 0x00800000, 0x00002000, 0x00802080 +}; + +static const uint32_t sbox5[64] = { + 0x00000100, 0x02080100, 0x02080000, 0x42000100, 0x00080000, 0x00000100, + 0x40000000, 0x02080000, 0x40080100, 0x00080000, 0x02000100, 0x40080100, + 0x42000100, 0x42080000, 0x00080100, 0x40000000, 0x02000000, 0x40080000, + 0x40080000, 0x00000000, 0x40000100, 0x42080100, 0x42080100, 0x02000100, + 0x42080000, 0x40000100, 0x00000000, 0x42000000, 0x02080100, 0x02000000, + 0x42000000, 0x00080100, 0x00080000, 0x42000100, 0x00000100, 0x02000000, + 0x40000000, 0x02080000, 0x42000100, 0x40080100, 0x02000100, 0x40000000, + 0x42080000, 0x02080100, 0x40080100, 0x00000100, 0x02000000, 0x42080000, + 0x42080100, 0x00080100, 0x42000000, 0x42080100, 0x02080000, 0x00000000, + 0x40080000, 0x42000000, 0x00080100, 0x02000100, 0x40000100, 0x00080000, + 0x00000000, 0x40080000, 0x02080100, 0x40000100 +}; + +static const uint32_t sbox6[64] = { + 0x20000010, 0x20400000, 0x00004000, 0x20404010, 0x20400000, 0x00000010, + 0x20404010, 0x00400000, 0x20004000, 0x00404010, 0x00400000, 0x20000010, + 0x00400010, 0x20004000, 0x20000000, 0x00004010, 0x00000000, 0x00400010, + 0x20004010, 0x00004000, 0x00404000, 0x20004010, 0x00000010, 0x20400010, + 0x20400010, 0x00000000, 0x00404010, 0x20404000, 0x00004010, 0x00404000, + 0x20404000, 0x20000000, 0x20004000, 0x00000010, 0x20400010, 0x00404000, + 0x20404010, 0x00400000, 0x00004010, 0x20000010, 0x00400000, 0x20004000, + 0x20000000, 0x00004010, 0x20000010, 0x20404010, 0x00404000, 0x20400000, + 0x00404010, 0x20404000, 0x00000000, 0x20400010, 0x00000010, 0x00004000, + 0x20400000, 0x00404010, 0x00004000, 0x00400010, 0x20004010, 0x00000000, + 0x20404000, 0x20000000, 0x00400010, 0x20004010 +}; + +static const uint32_t sbox7[64] = { + 0x00200000, 0x04200002, 0x04000802, 0x00000000, 0x00000800, 0x04000802, + 0x00200802, 0x04200800, 0x04200802, 0x00200000, 0x00000000, 0x04000002, + 0x00000002, 0x04000000, 0x04200002, 0x00000802, 0x04000800, 0x00200802, + 0x00200002, 0x04000800, 0x04000002, 0x04200000, 0x04200800, 0x00200002, + 0x04200000, 0x00000800, 0x00000802, 0x04200802, 0x00200800, 0x00000002, + 0x04000000, 0x00200800, 0x04000000, 0x00200800, 0x00200000, 0x04000802, + 0x04000802, 0x04200002, 0x04200002, 0x00000002, 0x00200002, 0x04000000, + 0x04000800, 0x00200000, 0x04200800, 0x00000802, 0x00200802, 0x04200800, + 0x00000802, 0x04000002, 0x04200802, 0x04200000, 0x00200800, 0x00000000, + 0x00000002, 0x04200802, 0x00000000, 0x00200802, 0x04200000, 0x00000800, + 0x04000002, 0x04000800, 0x00000800, 0x00200002 +}; + +static const uint32_t sbox8[64] = { + 0x10001040, 0x00001000, 0x00040000, 0x10041040, 0x10000000, 0x10001040, + 0x00000040, 0x10000000, 0x00040040, 0x10040000, 0x10041040, 0x00041000, + 0x10041000, 0x00041040, 0x00001000, 0x00000040, 0x10040000, 0x10000040, + 0x10001000, 0x00001040, 0x00041000, 0x00040040, 0x10040040, 0x10041000, + 0x00001040, 0x00000000, 0x00000000, 0x10040040, 0x10000040, 0x10001000, + 0x00041040, 0x00040000, 0x00041040, 0x00040000, 0x10041000, 0x00001000, + 0x00000040, 0x10040040, 0x00001000, 0x00041040, 0x10001000, 0x00000040, + 0x10000040, 0x10040000, 0x10040040, 0x10000000, 0x00040000, 0x10001040, + 0x00000000, 0x10041040, 0x00040040, 0x10000040, 0x10040000, 0x10001000, + 0x10001040, 0x00000000, 0x10041040, 0x00041000, 0x00041000, 0x00001040, + 0x00001040, 0x00040040, 0x10000000, 0x10041000 +}; + +/* + * These two tables are part of the 'permuted choice 1' function. + * In this implementation several speed improvements are done. + */ +static const uint32_t leftkey_swap[16] = { + 0x00000000, 0x00000001, 0x00000100, 0x00000101, + 0x00010000, 0x00010001, 0x00010100, 0x00010101, + 0x01000000, 0x01000001, 0x01000100, 0x01000101, + 0x01010000, 0x01010001, 0x01010100, 0x01010101 +}; + +static const uint32_t rightkey_swap[16] = { + 0x00000000, 0x01000000, 0x00010000, 0x01010000, + 0x00000100, 0x01000100, 0x00010100, 0x01010100, + 0x00000001, 0x01000001, 0x00010001, 0x01010001, + 0x00000101, 0x01000101, 0x00010101, 0x01010101, +}; + +/* + * Numbers of left shifts per round for encryption subkeys. To + * calculate the decryption subkeys we just reverse the ordering of + * the calculated encryption subkeys, so there is no need for a + * decryption rotate tab. + */ +static const unsigned char encrypt_rotate_tab[16] = { + 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 +}; + +/* + * Table with weak DES keys sorted in ascending order. In DES there + * are 64 known keys which are weak. They are weak because they + * produce only one, two or four different subkeys in the subkey + * scheduling process. The keys in this table have all their parity + * bits cleared. + */ +static const unsigned char weak_keys[64][8] = { + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*w */ + {0x00, 0x00, 0x1e, 0x1e, 0x00, 0x00, 0x0e, 0x0e}, + {0x00, 0x00, 0xe0, 0xe0, 0x00, 0x00, 0xf0, 0xf0}, + {0x00, 0x00, 0xfe, 0xfe, 0x00, 0x00, 0xfe, 0xfe}, + {0x00, 0x1e, 0x00, 0x1e, 0x00, 0x0e, 0x00, 0x0e}, /*sw */ + {0x00, 0x1e, 0x1e, 0x00, 0x00, 0x0e, 0x0e, 0x00}, + {0x00, 0x1e, 0xe0, 0xfe, 0x00, 0x0e, 0xf0, 0xfe}, + {0x00, 0x1e, 0xfe, 0xe0, 0x00, 0x0e, 0xfe, 0xf0}, + {0x00, 0xe0, 0x00, 0xe0, 0x00, 0xf0, 0x00, 0xf0}, /*sw */ + {0x00, 0xe0, 0x1e, 0xfe, 0x00, 0xf0, 0x0e, 0xfe}, + {0x00, 0xe0, 0xe0, 0x00, 0x00, 0xf0, 0xf0, 0x00}, + {0x00, 0xe0, 0xfe, 0x1e, 0x00, 0xf0, 0xfe, 0x0e}, + {0x00, 0xfe, 0x00, 0xfe, 0x00, 0xfe, 0x00, 0xfe}, /*sw */ + {0x00, 0xfe, 0x1e, 0xe0, 0x00, 0xfe, 0x0e, 0xf0}, + {0x00, 0xfe, 0xe0, 0x1e, 0x00, 0xfe, 0xf0, 0x0e}, + {0x00, 0xfe, 0xfe, 0x00, 0x00, 0xfe, 0xfe, 0x00}, + {0x1e, 0x00, 0x00, 0x1e, 0x0e, 0x00, 0x00, 0x0e}, + {0x1e, 0x00, 0x1e, 0x00, 0x0e, 0x00, 0x0e, 0x00}, /*sw */ + {0x1e, 0x00, 0xe0, 0xfe, 0x0e, 0x00, 0xf0, 0xfe}, + {0x1e, 0x00, 0xfe, 0xe0, 0x0e, 0x00, 0xfe, 0xf0}, + {0x1e, 0x1e, 0x00, 0x00, 0x0e, 0x0e, 0x00, 0x00}, + {0x1e, 0x1e, 0x1e, 0x1e, 0x0e, 0x0e, 0x0e, 0x0e}, /*w */ + {0x1e, 0x1e, 0xe0, 0xe0, 0x0e, 0x0e, 0xf0, 0xf0}, + {0x1e, 0x1e, 0xfe, 0xfe, 0x0e, 0x0e, 0xfe, 0xfe}, + {0x1e, 0xe0, 0x00, 0xfe, 0x0e, 0xf0, 0x00, 0xfe}, + {0x1e, 0xe0, 0x1e, 0xe0, 0x0e, 0xf0, 0x0e, 0xf0}, /*sw */ + {0x1e, 0xe0, 0xe0, 0x1e, 0x0e, 0xf0, 0xf0, 0x0e}, + {0x1e, 0xe0, 0xfe, 0x00, 0x0e, 0xf0, 0xfe, 0x00}, + {0x1e, 0xfe, 0x00, 0xe0, 0x0e, 0xfe, 0x00, 0xf0}, + {0x1e, 0xfe, 0x1e, 0xfe, 0x0e, 0xfe, 0x0e, 0xfe}, /*sw */ + {0x1e, 0xfe, 0xe0, 0x00, 0x0e, 0xfe, 0xf0, 0x00}, + {0x1e, 0xfe, 0xfe, 0x1e, 0x0e, 0xfe, 0xfe, 0x0e}, + {0xe0, 0x00, 0x00, 0xe0, 0xf0, 0x00, 0x00, 0xf0}, + {0xe0, 0x00, 0x1e, 0xfe, 0xf0, 0x00, 0x0e, 0xfe}, + {0xe0, 0x00, 0xe0, 0x00, 0xf0, 0x00, 0xf0, 0x00}, /*sw */ + {0xe0, 0x00, 0xfe, 0x1e, 0xf0, 0x00, 0xfe, 0x0e}, + {0xe0, 0x1e, 0x00, 0xfe, 0xf0, 0x0e, 0x00, 0xfe}, + {0xe0, 0x1e, 0x1e, 0xe0, 0xf0, 0x0e, 0x0e, 0xf0}, + {0xe0, 0x1e, 0xe0, 0x1e, 0xf0, 0x0e, 0xf0, 0x0e}, /*sw */ + {0xe0, 0x1e, 0xfe, 0x00, 0xf0, 0x0e, 0xfe, 0x00}, + {0xe0, 0xe0, 0x00, 0x00, 0xf0, 0xf0, 0x00, 0x00}, + {0xe0, 0xe0, 0x1e, 0x1e, 0xf0, 0xf0, 0x0e, 0x0e}, + {0xe0, 0xe0, 0xe0, 0xe0, 0xf0, 0xf0, 0xf0, 0xf0}, /*w */ + {0xe0, 0xe0, 0xfe, 0xfe, 0xf0, 0xf0, 0xfe, 0xfe}, + {0xe0, 0xfe, 0x00, 0x1e, 0xf0, 0xfe, 0x00, 0x0e}, + {0xe0, 0xfe, 0x1e, 0x00, 0xf0, 0xfe, 0x0e, 0x00}, + {0xe0, 0xfe, 0xe0, 0xfe, 0xf0, 0xfe, 0xf0, 0xfe}, /*sw */ + {0xe0, 0xfe, 0xfe, 0xe0, 0xf0, 0xfe, 0xfe, 0xf0}, + {0xfe, 0x00, 0x00, 0xfe, 0xfe, 0x00, 0x00, 0xfe}, + {0xfe, 0x00, 0x1e, 0xe0, 0xfe, 0x00, 0x0e, 0xf0}, + {0xfe, 0x00, 0xe0, 0x1e, 0xfe, 0x00, 0xf0, 0x0e}, + {0xfe, 0x00, 0xfe, 0x00, 0xfe, 0x00, 0xfe, 0x00}, /*sw */ + {0xfe, 0x1e, 0x00, 0xe0, 0xfe, 0x0e, 0x00, 0xf0}, + {0xfe, 0x1e, 0x1e, 0xfe, 0xfe, 0x0e, 0x0e, 0xfe}, + {0xfe, 0x1e, 0xe0, 0x00, 0xfe, 0x0e, 0xf0, 0x00}, + {0xfe, 0x1e, 0xfe, 0x1e, 0xfe, 0x0e, 0xfe, 0x0e}, /*sw */ + {0xfe, 0xe0, 0x00, 0x1e, 0xfe, 0xf0, 0x00, 0x0e}, + {0xfe, 0xe0, 0x1e, 0x00, 0xfe, 0xf0, 0x0e, 0x00}, + {0xfe, 0xe0, 0xe0, 0xfe, 0xfe, 0xf0, 0xf0, 0xfe}, + {0xfe, 0xe0, 0xfe, 0xe0, 0xfe, 0xf0, 0xfe, 0xf0}, /*sw */ + {0xfe, 0xfe, 0x00, 0x00, 0xfe, 0xfe, 0x00, 0x00}, + {0xfe, 0xfe, 0x1e, 0x1e, 0xfe, 0xfe, 0x0e, 0x0e}, + {0xfe, 0xfe, 0xe0, 0xe0, 0xfe, 0xfe, 0xf0, 0xf0}, + {0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe} /*w */ +}; + +bool +gl_des_is_weak_key (const char *key) +{ + char work[8]; + int i, left, right, middle, cmp_result; + + /* clear parity bits */ + for (i = 0; i < 8; ++i) + work[i] = ((unsigned char) key[i]) & 0xfe; + + /* binary search in the weak key table */ + left = 0; + right = 63; + while (left <= right) + { + middle = (left + right) / 2; + + if (!(cmp_result = memcmp (work, weak_keys[middle], 8))) + return -1; + + if (cmp_result > 0) + left = middle + 1; + else + right = middle - 1; + } + + return 0; +} + +/* + * Macro to swap bits across two words. + */ +#define DO_PERMUTATION(a, temp, b, offset, mask) \ + temp = ((a>>offset) ^ b) & mask; \ + b ^= temp; \ + a ^= temp<> 31); \ + temp = (left ^ right) & 0xaaaaaaaa; \ + right ^= temp; \ + left ^= temp; \ + left = (left << 1) | (left >> 31); + +/* + * The 'inverse initial permutation'. + */ +#define FINAL_PERMUTATION(left, temp, right) \ + left = (left << 31) | (left >> 1); \ + temp = (left ^ right) & 0xaaaaaaaa; \ + left ^= temp; \ + right ^= temp; \ + right = (right << 31) | (right >> 1); \ + DO_PERMUTATION(right, temp, left, 8, 0x00ff00ff) \ + DO_PERMUTATION(right, temp, left, 2, 0x33333333) \ + DO_PERMUTATION(left, temp, right, 16, 0x0000ffff) \ + DO_PERMUTATION(left, temp, right, 4, 0x0f0f0f0f) + + +/* + * A full DES round including 'expansion function', 'sbox substitution' + * and 'primitive function P' but without swapping the left and right word. + * Please note: The data in 'from' and 'to' is already rotated one bit to + * the left, done in the initial permutation. + */ +#define DES_ROUND(from, to, work, subkey) \ + work = from ^ *subkey++; \ + to ^= sbox8[ work & 0x3f ]; \ + to ^= sbox6[ (work>>8) & 0x3f ]; \ + to ^= sbox4[ (work>>16) & 0x3f ]; \ + to ^= sbox2[ (work>>24) & 0x3f ]; \ + work = ((from << 28) | (from >> 4)) ^ *subkey++; \ + to ^= sbox7[ work & 0x3f ]; \ + to ^= sbox5[ (work>>8) & 0x3f ]; \ + to ^= sbox3[ (work>>16) & 0x3f ]; \ + to ^= sbox1[ (work>>24) & 0x3f ]; + +/* + * Macros to convert 8 bytes from/to 32bit words. + */ +#define READ_64BIT_DATA(data, left, right) \ + left = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; \ + right = (data[4] << 24) | (data[5] << 16) | (data[6] << 8) | data[7]; + +#define WRITE_64BIT_DATA(data, left, right) \ + data[0] = (left >> 24) &0xff; data[1] = (left >> 16) &0xff; \ + data[2] = (left >> 8) &0xff; data[3] = left &0xff; \ + data[4] = (right >> 24) &0xff; data[5] = (right >> 16) &0xff; \ + data[6] = (right >> 8) &0xff; data[7] = right &0xff; + +/* + * des_key_schedule(): Calculate 16 subkeys pairs (even/odd) for + * 16 encryption rounds. + * To calculate subkeys for decryption the caller + * have to reorder the generated subkeys. + * + * rawkey: 8 Bytes of key data + * subkey: Array of at least 32 uint32_ts. Will be filled + * with calculated subkeys. + * + */ +static void +des_key_schedule (const char *_rawkey, uint32_t * subkey) +{ + const unsigned char *rawkey = (const unsigned char *) _rawkey; + uint32_t left, right, work; + int round; + + READ_64BIT_DATA (rawkey, left, right) + DO_PERMUTATION (right, work, left, 4, 0x0f0f0f0f) + DO_PERMUTATION (right, work, left, 0, 0x10101010) + left = ((leftkey_swap[(left >> 0) & 0xf] << 3) + | (leftkey_swap[(left >> 8) & 0xf] << 2) + | (leftkey_swap[(left >> 16) & 0xf] << 1) + | (leftkey_swap[(left >> 24) & 0xf]) + | (leftkey_swap[(left >> 5) & 0xf] << 7) + | (leftkey_swap[(left >> 13) & 0xf] << 6) + | (leftkey_swap[(left >> 21) & 0xf] << 5) + | (leftkey_swap[(left >> 29) & 0xf] << 4)); + + left &= 0x0fffffff; + + right = ((rightkey_swap[(right >> 1) & 0xf] << 3) + | (rightkey_swap[(right >> 9) & 0xf] << 2) + | (rightkey_swap[(right >> 17) & 0xf] << 1) + | (rightkey_swap[(right >> 25) & 0xf]) + | (rightkey_swap[(right >> 4) & 0xf] << 7) + | (rightkey_swap[(right >> 12) & 0xf] << 6) + | (rightkey_swap[(right >> 20) & 0xf] << 5) + | (rightkey_swap[(right >> 28) & 0xf] << 4)); + + right &= 0x0fffffff; + + for (round = 0; round < 16; ++round) + { + left = ((left << encrypt_rotate_tab[round]) + | (left >> (28 - encrypt_rotate_tab[round]))) & 0x0fffffff; + right = ((right << encrypt_rotate_tab[round]) + | (right >> (28 - encrypt_rotate_tab[round]))) & 0x0fffffff; + + *subkey++ = (((left << 4) & 0x24000000) + | ((left << 28) & 0x10000000) + | ((left << 14) & 0x08000000) + | ((left << 18) & 0x02080000) + | ((left << 6) & 0x01000000) + | ((left << 9) & 0x00200000) + | ((left >> 1) & 0x00100000) + | ((left << 10) & 0x00040000) + | ((left << 2) & 0x00020000) + | ((left >> 10) & 0x00010000) + | ((right >> 13) & 0x00002000) + | ((right >> 4) & 0x00001000) + | ((right << 6) & 0x00000800) + | ((right >> 1) & 0x00000400) + | ((right >> 14) & 0x00000200) + | (right & 0x00000100) + | ((right >> 5) & 0x00000020) + | ((right >> 10) & 0x00000010) + | ((right >> 3) & 0x00000008) + | ((right >> 18) & 0x00000004) + | ((right >> 26) & 0x00000002) + | ((right >> 24) & 0x00000001)); + + *subkey++ = (((left << 15) & 0x20000000) + | ((left << 17) & 0x10000000) + | ((left << 10) & 0x08000000) + | ((left << 22) & 0x04000000) + | ((left >> 2) & 0x02000000) + | ((left << 1) & 0x01000000) + | ((left << 16) & 0x00200000) + | ((left << 11) & 0x00100000) + | ((left << 3) & 0x00080000) + | ((left >> 6) & 0x00040000) + | ((left << 15) & 0x00020000) + | ((left >> 4) & 0x00010000) + | ((right >> 2) & 0x00002000) + | ((right << 8) & 0x00001000) + | ((right >> 14) & 0x00000808) + | ((right >> 9) & 0x00000400) + | ((right) & 0x00000200) + | ((right << 7) & 0x00000100) + | ((right >> 7) & 0x00000020) + | ((right >> 3) & 0x00000011) + | ((right << 2) & 0x00000004) + | ((right >> 21) & 0x00000002)); + } +} + +void +gl_des_setkey (gl_des_ctx * ctx, const char *key) +{ + int i; + + des_key_schedule (key, ctx->encrypt_subkeys); + + for (i = 0; i < 32; i += 2) + { + ctx->decrypt_subkeys[i] = ctx->encrypt_subkeys[30 - i]; + ctx->decrypt_subkeys[i + 1] = ctx->encrypt_subkeys[31 - i]; + } +} + +bool +gl_des_makekey (gl_des_ctx * ctx, const char *key, size_t keylen) +{ + if (keylen != 8) + return false; + + gl_des_setkey (ctx, key); + + return !gl_des_is_weak_key (key); +} + +void +gl_des_ecb_crypt (gl_des_ctx * ctx, const char *_from, char *_to, int mode) +{ + const unsigned char *from = (const unsigned char *) _from; + unsigned char *to = (unsigned char *) _to; + uint32_t left, right, work; + uint32_t *keys; + + keys = mode ? ctx->decrypt_subkeys : ctx->encrypt_subkeys; + +READ_64BIT_DATA (from, left, right) + INITIAL_PERMUTATION (left, work, right) + DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys) + DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys) + DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys) + DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys) + DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys) + DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys) + DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys) + DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys) + FINAL_PERMUTATION (right, work, left) WRITE_64BIT_DATA (to, right, left)} + +void +gl_3des_set2keys (gl_3des_ctx * ctx, const char *key1, const char *key2) +{ + int i; + + des_key_schedule (key1, ctx->encrypt_subkeys); + des_key_schedule (key2, &(ctx->decrypt_subkeys[32])); + + for (i = 0; i < 32; i += 2) + { + ctx->decrypt_subkeys[i] = ctx->encrypt_subkeys[30 - i]; + ctx->decrypt_subkeys[i + 1] = ctx->encrypt_subkeys[31 - i]; + + ctx->encrypt_subkeys[i + 32] = ctx->decrypt_subkeys[62 - i]; + ctx->encrypt_subkeys[i + 33] = ctx->decrypt_subkeys[63 - i]; + + ctx->encrypt_subkeys[i + 64] = ctx->encrypt_subkeys[i]; + ctx->encrypt_subkeys[i + 65] = ctx->encrypt_subkeys[i + 1]; + + ctx->decrypt_subkeys[i + 64] = ctx->decrypt_subkeys[i]; + ctx->decrypt_subkeys[i + 65] = ctx->decrypt_subkeys[i + 1]; + } +} + +void +gl_3des_set3keys (gl_3des_ctx * ctx, const char *key1, + const char *key2, const char *key3) +{ + int i; + + des_key_schedule (key1, ctx->encrypt_subkeys); + des_key_schedule (key2, &(ctx->decrypt_subkeys[32])); + des_key_schedule (key3, &(ctx->encrypt_subkeys[64])); + + for (i = 0; i < 32; i += 2) + { + ctx->decrypt_subkeys[i] = ctx->encrypt_subkeys[94 - i]; + ctx->decrypt_subkeys[i + 1] = ctx->encrypt_subkeys[95 - i]; + + ctx->encrypt_subkeys[i + 32] = ctx->decrypt_subkeys[62 - i]; + ctx->encrypt_subkeys[i + 33] = ctx->decrypt_subkeys[63 - i]; + + ctx->decrypt_subkeys[i + 64] = ctx->encrypt_subkeys[30 - i]; + ctx->decrypt_subkeys[i + 65] = ctx->encrypt_subkeys[31 - i]; + } +} + +void +gl_3des_ecb_crypt (gl_3des_ctx * ctx, const char *_from, char *_to, int mode) +{ + const unsigned char *from = (const unsigned char *) _from; + unsigned char *to = (unsigned char *) _to; + uint32_t left, right, work; + uint32_t *keys; + + keys = mode ? ctx->decrypt_subkeys : ctx->encrypt_subkeys; + +READ_64BIT_DATA (from, left, right) + INITIAL_PERMUTATION (left, work, right) + DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys) + DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys) + DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys) + DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys) + DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys) + DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys) + DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys) + DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys) + DES_ROUND (left, right, work, keys) DES_ROUND (right, left, work, keys) + DES_ROUND (left, right, work, keys) DES_ROUND (right, left, work, keys) + DES_ROUND (left, right, work, keys) DES_ROUND (right, left, work, keys) + DES_ROUND (left, right, work, keys) DES_ROUND (right, left, work, keys) + DES_ROUND (left, right, work, keys) DES_ROUND (right, left, work, keys) + DES_ROUND (left, right, work, keys) DES_ROUND (right, left, work, keys) + DES_ROUND (left, right, work, keys) DES_ROUND (right, left, work, keys) + DES_ROUND (left, right, work, keys) DES_ROUND (right, left, work, keys) + DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys) + DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys) + DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys) + DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys) + DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys) + DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys) + DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys) + DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys) + FINAL_PERMUTATION (right, work, left) WRITE_64BIT_DATA (to, right, left)} + +bool +gl_3des_makekey (gl_3des_ctx * ctx, const char *key, size_t keylen) +{ + if (keylen != 24) + return false; + + gl_3des_set3keys (ctx, key, key + 8, key + 16); + + return !(gl_des_is_weak_key (key) + || gl_des_is_weak_key (key + 8) || gl_des_is_weak_key (key + 16)); +} diff --git a/src/daemon/https/lgl/des.h b/src/daemon/https/lgl/des.h new file mode 100644 index 00000000..fdc8686f --- /dev/null +++ b/src/daemon/https/lgl/des.h @@ -0,0 +1,121 @@ +/* des.h --- DES cipher implementation. + * Copyright (C) 2005, 2007 Free Software Foundation, Inc. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2.1, or (at your + * option) any later version. + * + * This file 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 Lesser General Public License + * along with this file; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +/* Adapted for gnulib by Simon Josefsson, based on Libgcrypt. */ + +#ifndef DES_H +# define DES_H + +#include +#include +#include + +/* + * Encryption/Decryption context of DES + */ +typedef struct +{ + uint32_t encrypt_subkeys[32]; + uint32_t decrypt_subkeys[32]; +} gl_des_ctx; + +/* + * Encryption/Decryption context of Triple-DES + */ +typedef struct +{ + uint32_t encrypt_subkeys[96]; + uint32_t decrypt_subkeys[96]; +} gl_3des_ctx; + +/* Check whether the 8 byte key is weak. Does not check the parity + * bits of the key but simple ignore them. */ +extern bool +gl_des_is_weak_key (const char * key); + +/* + * DES + * --- + */ + +/* Fill a DES context CTX with subkeys calculated from 64bit KEY. + * Does not check parity bits, but simply ignore them. Does not check + * for weak keys. */ +extern void +gl_des_setkey (gl_des_ctx *ctx, const char * key); + +/* Fill a DES context CTX with subkeys calculated from 64bit KEY, with + * weak key checking. Does not check parity bits, but simply ignore + * them. */ +extern bool +gl_des_makekey (gl_des_ctx *ctx, const char * key, size_t keylen); + +/* Electronic Codebook Mode DES encryption/decryption of data + * according to 'mode'. */ +extern void +gl_des_ecb_crypt (gl_des_ctx *ctx, const char * from, char * to, int mode); + +#define gl_des_ecb_encrypt(ctx, from, to) gl_des_ecb_crypt(ctx, from, to, 0) +#define gl_des_ecb_decrypt(ctx, from, to) gl_des_ecb_crypt(ctx, from, to, 1) + +/* Triple-DES + * ---------- + */ + +/* Fill a Triple-DES context CTX with subkeys calculated from two + * 64bit keys in KEY1 and KEY2. Does not check the parity bits of the + * keys, but simply ignore them. Does not check for weak keys. */ +extern void +gl_3des_set2keys (gl_3des_ctx *ctx, + const char * key1, + const char * key2); + +/* + * Fill a Triple-DES context CTX with subkeys calculated from three + * 64bit keys in KEY1, KEY2 and KEY3. Does not check the parity bits + * of the keys, but simply ignore them. Does not check for weak + * keys. */ +extern void +gl_3des_set3keys (gl_3des_ctx *ctx, + const char * key1, + const char * key2, + const char * key3); + +/* Fill a Triple-DES context CTX with subkeys calculated from three + * concatenated 64bit keys in KEY, with weak key checking. Does not + * check the parity bits of the keys, but simply ignore them. */ +extern bool +gl_3des_makekey (gl_3des_ctx *ctx, + const char * key, + size_t keylen); + +/* Electronic Codebook Mode Triple-DES encryption/decryption of data + * according to 'mode'. Sometimes this mode is named 'EDE' mode + * (Encryption-Decryption-Encryption). */ +extern void +gl_3des_ecb_crypt (gl_3des_ctx *ctx, + const char * from, + char * to, + int mode); + +#define gl_3des_ecb_encrypt(ctx, from, to) gl_3des_ecb_crypt(ctx,from,to,0) +#define gl_3des_ecb_decrypt(ctx, from, to) gl_3des_ecb_crypt(ctx,from,to,1) + +#endif /* DES_H */ diff --git a/src/daemon/https/lgl/float+.h b/src/daemon/https/lgl/float+.h new file mode 100644 index 00000000..f0c0c189 --- /dev/null +++ b/src/daemon/https/lgl/float+.h @@ -0,0 +1,148 @@ +/* Supplemental information about the floating-point formats. + Copyright (C) 2007 Free Software Foundation, Inc. + Written by Bruno Haible , 2007. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1, or (at your option) + any later version. + + This program 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef _FLOATPLUS_H +#define _FLOATPLUS_H + +#include +#include + +/* Number of bits in the mantissa of a floating-point number, including the + "hidden bit". */ +#if FLT_RADIX == 2 +# define FLT_MANT_BIT FLT_MANT_DIG +# define DBL_MANT_BIT DBL_MANT_DIG +# define LDBL_MANT_BIT LDBL_MANT_DIG +#elif FLT_RADIX == 4 +# define FLT_MANT_BIT (FLT_MANT_DIG * 2) +# define DBL_MANT_BIT (DBL_MANT_DIG * 2) +# define LDBL_MANT_BIT (LDBL_MANT_DIG * 2) +#elif FLT_RADIX == 16 +# define FLT_MANT_BIT (FLT_MANT_DIG * 4) +# define DBL_MANT_BIT (DBL_MANT_DIG * 4) +# define LDBL_MANT_BIT (LDBL_MANT_DIG * 4) +#endif + +/* Bit mask that can be used to mask the exponent, as an unsigned number. */ +#define FLT_EXP_MASK ((FLT_MAX_EXP - FLT_MIN_EXP) | 7) +#define DBL_EXP_MASK ((DBL_MAX_EXP - DBL_MIN_EXP) | 7) +#define LDBL_EXP_MASK ((LDBL_MAX_EXP - LDBL_MIN_EXP) | 7) + +/* Number of bits used for the exponent of a floating-point number, including + the exponent's sign. */ +#define FLT_EXP_BIT \ + (FLT_EXP_MASK < 0x100 ? 8 : \ + FLT_EXP_MASK < 0x200 ? 9 : \ + FLT_EXP_MASK < 0x400 ? 10 : \ + FLT_EXP_MASK < 0x800 ? 11 : \ + FLT_EXP_MASK < 0x1000 ? 12 : \ + FLT_EXP_MASK < 0x2000 ? 13 : \ + FLT_EXP_MASK < 0x4000 ? 14 : \ + FLT_EXP_MASK < 0x8000 ? 15 : \ + FLT_EXP_MASK < 0x10000 ? 16 : \ + FLT_EXP_MASK < 0x20000 ? 17 : \ + FLT_EXP_MASK < 0x40000 ? 18 : \ + FLT_EXP_MASK < 0x80000 ? 19 : \ + FLT_EXP_MASK < 0x100000 ? 20 : \ + FLT_EXP_MASK < 0x200000 ? 21 : \ + FLT_EXP_MASK < 0x400000 ? 22 : \ + FLT_EXP_MASK < 0x800000 ? 23 : \ + FLT_EXP_MASK < 0x1000000 ? 24 : \ + FLT_EXP_MASK < 0x2000000 ? 25 : \ + FLT_EXP_MASK < 0x4000000 ? 26 : \ + FLT_EXP_MASK < 0x8000000 ? 27 : \ + FLT_EXP_MASK < 0x10000000 ? 28 : \ + FLT_EXP_MASK < 0x20000000 ? 29 : \ + FLT_EXP_MASK < 0x40000000 ? 30 : \ + FLT_EXP_MASK <= 0x7fffffff ? 31 : \ + 32) +#define DBL_EXP_BIT \ + (DBL_EXP_MASK < 0x100 ? 8 : \ + DBL_EXP_MASK < 0x200 ? 9 : \ + DBL_EXP_MASK < 0x400 ? 10 : \ + DBL_EXP_MASK < 0x800 ? 11 : \ + DBL_EXP_MASK < 0x1000 ? 12 : \ + DBL_EXP_MASK < 0x2000 ? 13 : \ + DBL_EXP_MASK < 0x4000 ? 14 : \ + DBL_EXP_MASK < 0x8000 ? 15 : \ + DBL_EXP_MASK < 0x10000 ? 16 : \ + DBL_EXP_MASK < 0x20000 ? 17 : \ + DBL_EXP_MASK < 0x40000 ? 18 : \ + DBL_EXP_MASK < 0x80000 ? 19 : \ + DBL_EXP_MASK < 0x100000 ? 20 : \ + DBL_EXP_MASK < 0x200000 ? 21 : \ + DBL_EXP_MASK < 0x400000 ? 22 : \ + DBL_EXP_MASK < 0x800000 ? 23 : \ + DBL_EXP_MASK < 0x1000000 ? 24 : \ + DBL_EXP_MASK < 0x2000000 ? 25 : \ + DBL_EXP_MASK < 0x4000000 ? 26 : \ + DBL_EXP_MASK < 0x8000000 ? 27 : \ + DBL_EXP_MASK < 0x10000000 ? 28 : \ + DBL_EXP_MASK < 0x20000000 ? 29 : \ + DBL_EXP_MASK < 0x40000000 ? 30 : \ + DBL_EXP_MASK <= 0x7fffffff ? 31 : \ + 32) +#define LDBL_EXP_BIT \ + (LDBL_EXP_MASK < 0x100 ? 8 : \ + LDBL_EXP_MASK < 0x200 ? 9 : \ + LDBL_EXP_MASK < 0x400 ? 10 : \ + LDBL_EXP_MASK < 0x800 ? 11 : \ + LDBL_EXP_MASK < 0x1000 ? 12 : \ + LDBL_EXP_MASK < 0x2000 ? 13 : \ + LDBL_EXP_MASK < 0x4000 ? 14 : \ + LDBL_EXP_MASK < 0x8000 ? 15 : \ + LDBL_EXP_MASK < 0x10000 ? 16 : \ + LDBL_EXP_MASK < 0x20000 ? 17 : \ + LDBL_EXP_MASK < 0x40000 ? 18 : \ + LDBL_EXP_MASK < 0x80000 ? 19 : \ + LDBL_EXP_MASK < 0x100000 ? 20 : \ + LDBL_EXP_MASK < 0x200000 ? 21 : \ + LDBL_EXP_MASK < 0x400000 ? 22 : \ + LDBL_EXP_MASK < 0x800000 ? 23 : \ + LDBL_EXP_MASK < 0x1000000 ? 24 : \ + LDBL_EXP_MASK < 0x2000000 ? 25 : \ + LDBL_EXP_MASK < 0x4000000 ? 26 : \ + LDBL_EXP_MASK < 0x8000000 ? 27 : \ + LDBL_EXP_MASK < 0x10000000 ? 28 : \ + LDBL_EXP_MASK < 0x20000000 ? 29 : \ + LDBL_EXP_MASK < 0x40000000 ? 30 : \ + LDBL_EXP_MASK <= 0x7fffffff ? 31 : \ + 32) + +/* Number of bits used for a floating-point number: the mantissa (not + counting the "hidden bit", since it may or may not be explicit), the + exponent, and the sign. */ +#define FLT_TOTAL_BIT ((FLT_MANT_BIT - 1) + FLT_EXP_BIT + 1) +#define DBL_TOTAL_BIT ((DBL_MANT_BIT - 1) + DBL_EXP_BIT + 1) +#define LDBL_TOTAL_BIT ((LDBL_MANT_BIT - 1) + LDBL_EXP_BIT + 1) + +/* Number of bytes used for a floating-point number. + This can be smaller than the 'sizeof'. For example, on i386 systems, + 'long double' most often have LDBL_MANT_BIT = 64, LDBL_EXP_BIT = 16, hence + LDBL_TOTAL_BIT = 80 bits, i.e. 10 bytes of consecutive memory, but + sizeof (long double) = 12 or = 16. */ +#define SIZEOF_FLT ((FLT_TOTAL_BIT + CHAR_BIT - 1) / CHAR_BIT) +#define SIZEOF_DBL ((DBL_TOTAL_BIT + CHAR_BIT - 1) / CHAR_BIT) +#define SIZEOF_LDBL ((LDBL_TOTAL_BIT + CHAR_BIT - 1) / CHAR_BIT) + +/* Verify that SIZEOF_FLT <= sizeof (float) etc. */ +typedef int verify_sizeof_flt[2 * (SIZEOF_FLT <= sizeof (float)) - 1]; +typedef int verify_sizeof_dbl[2 * (SIZEOF_DBL <= sizeof (double)) - 1]; +typedef int verify_sizeof_ldbl[2 * (SIZEOF_LDBL <= sizeof (long double)) - 1]; + +#endif /* _FLOATPLUS_H */ diff --git a/src/daemon/https/lgl/gc-gnulib.c b/src/daemon/https/lgl/gc-gnulib.c new file mode 100644 index 00000000..05b12781 --- /dev/null +++ b/src/daemon/https/lgl/gc-gnulib.c @@ -0,0 +1,791 @@ +/* gc-gnulib.c --- Common gnulib internal crypto interface functions + * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Simon Josefsson + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2.1, or (at your + * option) any later version. + * + * This file 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 Lesser General Public License + * along with this file; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +/* Note: This file is only built if GC uses internal functions. */ + +#include + +/* Get prototype. */ +#include "gc.h" + +#include +#include + +/* For randomize. */ +#ifdef GNULIB_GC_RANDOM +# include +# include +# include +# include +# include +#endif + +/* Hashes. */ +#ifdef GNULIB_GC_MD5 +# include "md5.h" +#endif +#ifdef GNULIB_GC_SHA1 +# include "sha1.h" +#endif +#if defined(GNULIB_GC_HMAC_MD5) || defined(GNULIB_GC_HMAC_SHA1) +# include "hmac.h" +#endif + +/* Ciphers. */ +#ifdef GNULIB_GC_ARCFOUR +# include "arcfour.h" +#endif +#ifdef GNULIB_GC_ARCTWO +# include "arctwo.h" +#endif +#ifdef GNULIB_GC_DES +# include "des.h" +#endif +#ifdef GNULIB_GC_RIJNDAEL +# include "rijndael-api-fst.h" +#endif + +/* The results of open() in this file are not used with fchdir, + therefore save some unnecessary work in fchdir.c. */ +#undef open +#undef close + +Gc_rc gc_init(void) +{ + return GC_OK; +} + +void gc_done(void) +{ + return; +} + +#ifdef GNULIB_GC_RANDOM + +/* Randomness. */ + +static Gc_rc +randomize (int level, char *data, size_t datalen) + { + int fd; + const char *device; + size_t len = 0; + int rc; + + switch (level) + { + case 0: + device = NAME_OF_NONCE_DEVICE; + break; + + case 1: + device = NAME_OF_PSEUDO_RANDOM_DEVICE; + break; + + default: + device = NAME_OF_RANDOM_DEVICE; + break; + } + + if (strcmp (device, "no") == 0) + return GC_RANDOM_ERROR; + + fd = open (device, O_RDONLY); + if (fd < 0) + return GC_RANDOM_ERROR; + + do + { + ssize_t tmp; + + tmp = read (fd, data, datalen); + + if (tmp < 0) + { + int save_errno = errno; + close (fd); + errno = save_errno; + return GC_RANDOM_ERROR; + } + + len += tmp; + } + while (len < datalen); + + rc = close (fd); + if (rc < 0) + return GC_RANDOM_ERROR; + + return GC_OK; + } + +Gc_rc +gc_nonce (char *data, size_t datalen) + { + return randomize (0, data, datalen); + } + +Gc_rc +gc_pseudo_random (char *data, size_t datalen) + { + return randomize (1, data, datalen); + } + +Gc_rc +gc_random (char *data, size_t datalen) + { + return randomize (2, data, datalen); + } + +#endif + +/* Memory allocation. */ +void gc_set_allocators(gc_malloc_t func_malloc, + gc_malloc_t secure_malloc, + gc_secure_check_t secure_check, + gc_realloc_t func_realloc, + gc_free_t func_free) +{ + return; +} + +/* Ciphers. */ + +typedef struct _gc_cipher_ctx + { + Gc_cipher alg; + Gc_cipher_mode mode; +#ifdef GNULIB_GC_ARCTWO + arctwo_context arctwoContext; + char arctwoIV[ARCTWO_BLOCK_SIZE]; +#endif +#ifdef GNULIB_GC_ARCFOUR + arcfour_context arcfourContext; +#endif +#ifdef GNULIB_GC_DES + gl_des_ctx desContext; +#endif +#ifdef GNULIB_GC_RIJNDAEL + rijndaelKeyInstance aesEncKey; + rijndaelKeyInstance aesDecKey; + rijndaelCipherInstance aesContext; +#endif + } _gc_cipher_ctx; + +Gc_rc gc_cipher_open(Gc_cipher alg, + Gc_cipher_mode mode, + gc_cipher_handle * outhandle) +{ + _gc_cipher_ctx *ctx; + Gc_rc rc = GC_OK; + + ctx = calloc(sizeof (*ctx), 1); + if (!ctx) + return GC_MALLOC_ERROR; + + ctx->alg = alg; + ctx->mode = mode; + + switch (alg) + { +#ifdef GNULIB_GC_ARCTWO + case GC_ARCTWO40: + switch (mode) + { + case GC_ECB: + case GC_CBC: + break; + + default: + rc = GC_INVALID_CIPHER; + } + break; +#endif + +#ifdef GNULIB_GC_ARCFOUR + case GC_ARCFOUR128: + case GC_ARCFOUR40: + switch (mode) + { + case GC_STREAM: + break; + + default: + rc = GC_INVALID_CIPHER; + } + break; +#endif + +#ifdef GNULIB_GC_DES + case GC_DES: + switch (mode) + { + case GC_ECB: + break; + + default: + rc = GC_INVALID_CIPHER; + } + break; +#endif + +#ifdef GNULIB_GC_RIJNDAEL + case GC_AES128: + case GC_AES192: + case GC_AES256: + switch (mode) + { + case GC_ECB: + case GC_CBC: + break; + + default: + rc = GC_INVALID_CIPHER; + } + break; +#endif + + default: + rc = GC_INVALID_CIPHER; + } + + if (rc == GC_OK) + *outhandle = ctx; + else + free(ctx); + + return rc; +} + +Gc_rc gc_cipher_setkey(gc_cipher_handle handle, + size_t keylen, + const char *key) +{ + _gc_cipher_ctx *ctx = handle; + + switch (ctx->alg) + { +#ifdef GNULIB_GC_ARCTWO + case GC_ARCTWO40: + arctwo_setkey (&ctx->arctwoContext, keylen, key); + break; +#endif + +#ifdef GNULIB_GC_ARCFOUR + case GC_ARCFOUR128: + case GC_ARCFOUR40: + arcfour_setkey (&ctx->arcfourContext, key, keylen); + break; +#endif + +#ifdef GNULIB_GC_DES + case GC_DES: + if (keylen != 8) + return GC_INVALID_CIPHER; + gl_des_setkey (&ctx->desContext, key); + break; +#endif + +#ifdef GNULIB_GC_RIJNDAEL + case GC_AES128: + case GC_AES192: + case GC_AES256: + { + rijndael_rc rc; + size_t i; + char keyMaterial[RIJNDAEL_MAX_KEY_SIZE + 1]; + + for (i = 0; i < keylen; i++) + sprintf (&keyMaterial[2 * i], "%02x", key[i] & 0xFF); + + rc = rijndaelMakeKey (&ctx->aesEncKey, RIJNDAEL_DIR_ENCRYPT, + keylen * 8, keyMaterial); + if (rc < 0) + return GC_INVALID_CIPHER; + + rc = rijndaelMakeKey (&ctx->aesDecKey, RIJNDAEL_DIR_DECRYPT, + keylen * 8, keyMaterial); + if (rc < 0) + return GC_INVALID_CIPHER; + + rc = rijndaelCipherInit (&ctx->aesContext, RIJNDAEL_MODE_ECB, NULL); + if (rc < 0) + return GC_INVALID_CIPHER; + } + break; +#endif + + default: + return GC_INVALID_CIPHER; + } + + return GC_OK; +} + +Gc_rc gc_cipher_setiv(gc_cipher_handle handle, + size_t ivlen, + const char *iv) +{ + _gc_cipher_ctx *ctx = handle; + + switch (ctx->alg) + { +#ifdef GNULIB_GC_ARCTWO + case GC_ARCTWO40: + if (ivlen != ARCTWO_BLOCK_SIZE) + return GC_INVALID_CIPHER; + memcpy (ctx->arctwoIV, iv, ivlen); + break; +#endif + +#ifdef GNULIB_GC_RIJNDAEL + case GC_AES128: + case GC_AES192: + case GC_AES256: + switch (ctx->mode) + { + case GC_ECB: + /* Doesn't use IV. */ + break; + + case GC_CBC: + { + rijndael_rc rc; + size_t i; + char ivMaterial[2 * RIJNDAEL_MAX_IV_SIZE + 1]; + + for (i = 0; i < ivlen; i++) + sprintf (&ivMaterial[2 * i], "%02x", iv[i] & 0xFF); + + rc = rijndaelCipherInit (&ctx->aesContext, RIJNDAEL_MODE_CBC, + ivMaterial); + if (rc < 0) + return GC_INVALID_CIPHER; + } + break; + + default: + return GC_INVALID_CIPHER; + } + break; +#endif + + default: + return GC_INVALID_CIPHER; + } + + return GC_OK; +} + +Gc_rc gc_cipher_encrypt_inline(gc_cipher_handle handle, + size_t len, + char *data) +{ + _gc_cipher_ctx *ctx = handle; + + switch (ctx->alg) + { +#ifdef GNULIB_GC_ARCTWO + case GC_ARCTWO40: + switch (ctx->mode) + { + case GC_ECB: + arctwo_encrypt (&ctx->arctwoContext, data, data, len); + break; + + case GC_CBC: + for (; len >= ARCTWO_BLOCK_SIZE; len -= ARCTWO_BLOCK_SIZE, + data += ARCTWO_BLOCK_SIZE) + { + size_t i; + for (i = 0; i < ARCTWO_BLOCK_SIZE; i++) + data[i] ^= ctx->arctwoIV[i]; + arctwo_encrypt (&ctx->arctwoContext, data, data, + ARCTWO_BLOCK_SIZE); + memcpy (ctx->arctwoIV, data, ARCTWO_BLOCK_SIZE); + } + break; + + default: + return GC_INVALID_CIPHER; + } + break; +#endif + +#ifdef GNULIB_GC_ARCFOUR + case GC_ARCFOUR128: + case GC_ARCFOUR40: + arcfour_stream (&ctx->arcfourContext, data, data, len); + break; +#endif + +#ifdef GNULIB_GC_DES + case GC_DES: + for (; len >= 8; len -= 8, data += 8) + gl_des_ecb_encrypt (&ctx->desContext, data, data); + break; +#endif + +#ifdef GNULIB_GC_RIJNDAEL + case GC_AES128: + case GC_AES192: + case GC_AES256: + { + int nblocks; + + nblocks = rijndaelBlockEncrypt (&ctx->aesContext, &ctx->aesEncKey, + data, 8 * len, data); + if (nblocks < 0) + return GC_INVALID_CIPHER; + } + break; +#endif + + default: + return GC_INVALID_CIPHER; + } + + return GC_OK; +} + +Gc_rc gc_cipher_decrypt_inline(gc_cipher_handle handle, + size_t len, + char *data) +{ + _gc_cipher_ctx *ctx = handle; + + switch (ctx->alg) + { +#ifdef GNULIB_GC_ARCTWO + case GC_ARCTWO40: + switch (ctx->mode) + { + case GC_ECB: + arctwo_decrypt (&ctx->arctwoContext, data, data, len); + break; + + case GC_CBC: + for (; len >= ARCTWO_BLOCK_SIZE; len -= ARCTWO_BLOCK_SIZE, + data += ARCTWO_BLOCK_SIZE) + { + char tmpIV[ARCTWO_BLOCK_SIZE]; + size_t i; + memcpy (tmpIV, data, ARCTWO_BLOCK_SIZE); + arctwo_decrypt (&ctx->arctwoContext, data, data, + ARCTWO_BLOCK_SIZE); + for (i = 0; i < ARCTWO_BLOCK_SIZE; i++) + data[i] ^= ctx->arctwoIV[i]; + memcpy (ctx->arctwoIV, tmpIV, ARCTWO_BLOCK_SIZE); + } + break; + + default: + return GC_INVALID_CIPHER; + } + break; +#endif + +#ifdef GNULIB_GC_ARCFOUR + case GC_ARCFOUR128: + case GC_ARCFOUR40: + arcfour_stream (&ctx->arcfourContext, data, data, len); + break; +#endif + +#ifdef GNULIB_GC_DES + case GC_DES: + for (; len >= 8; len -= 8, data += 8) + gl_des_ecb_decrypt (&ctx->desContext, data, data); + break; +#endif + +#ifdef GNULIB_GC_RIJNDAEL + case GC_AES128: + case GC_AES192: + case GC_AES256: + { + int nblocks; + + nblocks = rijndaelBlockDecrypt (&ctx->aesContext, &ctx->aesDecKey, + data, 8 * len, data); + if (nblocks < 0) + return GC_INVALID_CIPHER; + } + break; +#endif + + default: + return GC_INVALID_CIPHER; + } + + return GC_OK; +} + +Gc_rc gc_cipher_close(gc_cipher_handle handle) +{ + _gc_cipher_ctx *ctx = handle; + + if (ctx) + free(ctx); + + return GC_OK; +} + +/* Hashes. */ + +#define MAX_DIGEST_SIZE 20 + +typedef struct _gc_hash_ctx + { + Gc_hash alg; + Gc_hash_mode mode; + char hash[MAX_DIGEST_SIZE]; +#ifdef GNULIB_GC_MD5 + struct md5_ctx md5Context; +#endif +#ifdef GNULIB_GC_SHA1 + struct sha1_ctx sha1Context; +#endif + } _gc_hash_ctx; + +Gc_rc gc_hash_open(Gc_hash hash, + Gc_hash_mode mode, + gc_hash_handle * outhandle) +{ + _gc_hash_ctx *ctx; + Gc_rc rc = GC_OK; + + ctx = calloc(sizeof (*ctx), 1); + if (!ctx) + return GC_MALLOC_ERROR; + + ctx->alg = hash; + ctx->mode = mode; + + switch (hash) + { +#ifdef GNULIB_GC_MD5 + case GC_MD5: + md5_init_ctx (&ctx->md5Context); + break; +#endif + +#ifdef GNULIB_GC_SHA1 + case GC_SHA1: + sha1_init_ctx (&ctx->sha1Context); + break; +#endif + + default: + rc = GC_INVALID_HASH; + break; + } + + switch (mode) + { + case 0: + break; + + default: + rc = GC_INVALID_HASH; + break; + } + + if (rc == GC_OK) + *outhandle = ctx; + else + free(ctx); + + return rc; +} + +Gc_rc gc_hash_clone(gc_hash_handle handle, + gc_hash_handle * outhandle) +{ + _gc_hash_ctx *in = handle; + _gc_hash_ctx *out; + + *outhandle = out = calloc(sizeof (*out), 1); + if (!out) + return GC_MALLOC_ERROR; + + memcpy(out, in, sizeof (*out)); + + return GC_OK; +} + +size_t gc_hash_digest_length(Gc_hash hash) +{ + size_t len; + + switch (hash) + { + case GC_MD2: + len = GC_MD2_DIGEST_SIZE; + break; + + case GC_MD4: + len = GC_MD4_DIGEST_SIZE; + break; + + case GC_MD5: + len = GC_MD5_DIGEST_SIZE; + break; + + case GC_RMD160: + len = GC_RMD160_DIGEST_SIZE; + break; + + case GC_SHA1: + len = GC_SHA1_DIGEST_SIZE; + break; + + default: + return 0; + } + + return len; +} + +void gc_hash_write(gc_hash_handle handle, + size_t len, + const char *data) +{ + _gc_hash_ctx *ctx = handle; + + switch (ctx->alg) + { +#ifdef GNULIB_GC_MD5 + case GC_MD5: + md5_process_bytes (data, len, &ctx->md5Context); + break; +#endif + +#ifdef GNULIB_GC_SHA1 + case GC_SHA1: + sha1_process_bytes (data, len, &ctx->sha1Context); + break; +#endif + + default: + break; + } +} + +const char * gc_hash_read(gc_hash_handle handle) +{ + _gc_hash_ctx *ctx = handle; + const char *ret= NULL; + + switch (ctx->alg) + { +#ifdef GNULIB_GC_MD5 + case GC_MD5: + md5_finish_ctx (&ctx->md5Context, ctx->hash); + ret = ctx->hash; + break; +#endif + +#ifdef GNULIB_GC_SHA1 + case GC_SHA1: + sha1_finish_ctx (&ctx->sha1Context, ctx->hash); + ret = ctx->hash; + break; +#endif + + default: + return NULL; + } + + return ret; +} + +void gc_hash_close(gc_hash_handle handle) +{ + _gc_hash_ctx *ctx = handle; + + free(ctx); +} + +Gc_rc gc_hash_buffer(Gc_hash hash, + const void *in, + size_t inlen, + char *resbuf) +{ + switch (hash) + { +#ifdef GNULIB_GC_MD5 + case GC_MD5: + md5_buffer (in, inlen, resbuf); + break; +#endif + +#ifdef GNULIB_GC_SHA1 + case GC_SHA1: + sha1_buffer (in, inlen, resbuf); + break; +#endif + + default: + return GC_INVALID_HASH; + } + + return GC_OK; +} + +#ifdef GNULIB_GC_MD5 +Gc_rc +gc_md5 (const void *in, size_t inlen, void *resbuf) + { + md5_buffer (in, inlen, resbuf); + return GC_OK; + } +#endif + +#ifdef GNULIB_GC_SHA1 +Gc_rc +gc_sha1 (const void *in, size_t inlen, void *resbuf) + { + sha1_buffer (in, inlen, resbuf); + return GC_OK; + } +#endif + +#ifdef GNULIB_GC_HMAC_MD5 +Gc_rc +gc_hmac_md5 (const void *key, size_t keylen, + const void *in, size_t inlen, char *resbuf) + { + hmac_md5 (key, keylen, in, inlen, resbuf); + return GC_OK; + } +#endif + +#ifdef GNULIB_GC_HMAC_SHA1 +Gc_rc gc_hmac_sha1(const void *key, + size_t keylen, + const void *in, + size_t inlen, + char *resbuf) +{ + hmac_sha1(key, keylen, in, inlen, resbuf); + return GC_OK; +} +#endif diff --git a/src/daemon/https/lgl/gc-libgcrypt.c b/src/daemon/https/lgl/gc-libgcrypt.c new file mode 100644 index 00000000..da9c5e16 --- /dev/null +++ b/src/daemon/https/lgl/gc-libgcrypt.c @@ -0,0 +1,627 @@ +/* gc-libgcrypt.c --- Crypto wrappers around Libgcrypt for GC. + * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Simon Josefsson + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2.1, or (at your + * option) any later version. + * + * This file 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 Lesser General Public License + * along with this file; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +/* Note: This file is only built if GC uses Libgcrypt. */ + +#include "config.h" + +/* Get prototype. */ +#include "gc.h" + +#include +#include + +/* Get libgcrypt API. */ +#include + +#include + +/* Initialization. */ + +Gc_rc gc_init(void) +{ + gcry_error_t err; + + err = gcry_control(GCRYCTL_ANY_INITIALIZATION_P); + if (err == GPG_ERR_NO_ERROR) + { + if (gcry_check_version(GCRYPT_VERSION) == NULL) + return GC_INIT_ERROR; + + err = gcry_control(GCRYCTL_INITIALIZATION_FINISHED, NULL, 0); + if (err != GPG_ERR_NO_ERROR) + return GC_INIT_ERROR; + } + return GC_OK; +} + +void gc_done(void) +{ + return; +} + +#ifdef GNULIB_GC_RANDOM + +/* Randomness. */ + +Gc_rc +gc_nonce (char *data, size_t datalen) + { + gcry_create_nonce ((unsigned char *) data, datalen); + return GC_OK; + } + +Gc_rc +gc_pseudo_random (char *data, size_t datalen) + { + gcry_randomize ((unsigned char *) data, datalen, GCRY_STRONG_RANDOM); + return GC_OK; + } + +Gc_rc +gc_random (char *data, size_t datalen) + { + gcry_randomize ((unsigned char *) data, datalen, GCRY_VERY_STRONG_RANDOM); + return GC_OK; + } + +#endif + +/* Memory allocation. */ + +void gc_set_allocators(gc_malloc_t func_malloc, + gc_malloc_t secure_malloc, + gc_secure_check_t secure_check, + gc_realloc_t func_realloc, + gc_free_t func_free) +{ + gcry_set_allocation_handler(func_malloc, secure_malloc, secure_check, + func_realloc, func_free); +} + +/* Ciphers. */ + +Gc_rc gc_cipher_open(Gc_cipher alg, + Gc_cipher_mode mode, + gc_cipher_handle * outhandle) +{ + int gcryalg, gcrymode; + gcry_error_t err; + + switch (alg) + { + case GC_AES128: + gcryalg = GCRY_CIPHER_RIJNDAEL; + break; + + case GC_AES192: + gcryalg = GCRY_CIPHER_RIJNDAEL; + break; + + case GC_AES256: + gcryalg = GCRY_CIPHER_RIJNDAEL256; + break; + + case GC_3DES: + gcryalg = GCRY_CIPHER_3DES; + break; + + case GC_DES: + gcryalg = GCRY_CIPHER_DES; + break; + + case GC_ARCFOUR128: + case GC_ARCFOUR40: + gcryalg = GCRY_CIPHER_ARCFOUR; + break; + + case GC_ARCTWO40: + gcryalg = GCRY_CIPHER_RFC2268_40; + break; + +#ifdef ENABLE_CAMELLIA + case GC_CAMELLIA128: + gcryalg = GCRY_CIPHER_CAMELLIA128; + break; + + case GC_CAMELLIA256: + gcryalg = GCRY_CIPHER_CAMELLIA256; + break; +#endif + + default: + return GC_INVALID_CIPHER; + } + + switch (mode) + { + case GC_ECB: + gcrymode = GCRY_CIPHER_MODE_ECB; + break; + + case GC_CBC: + gcrymode = GCRY_CIPHER_MODE_CBC; + break; + + case GC_STREAM: + gcrymode = GCRY_CIPHER_MODE_STREAM; + break; + + default: + return GC_INVALID_CIPHER; + } + + err = gcry_cipher_open((gcry_cipher_hd_t *) outhandle, gcryalg, gcrymode, 0); + if (gcry_err_code(err)) + return GC_INVALID_CIPHER; + + return GC_OK; +} + +Gc_rc gc_cipher_setkey(gc_cipher_handle handle, + size_t keylen, + const char *key) +{ + gcry_error_t err; + + err = gcry_cipher_setkey ((gcry_cipher_hd_t) handle, key, keylen); + if (gcry_err_code(err)) + return GC_INVALID_CIPHER; + + return GC_OK; +} + +Gc_rc gc_cipher_setiv(gc_cipher_handle handle, + size_t ivlen, + const char *iv) +{ + gcry_error_t err; + + err = gcry_cipher_setiv ((gcry_cipher_hd_t) handle, iv, ivlen); + if (gcry_err_code(err)) + return GC_INVALID_CIPHER; + + return GC_OK; +} + +Gc_rc gc_cipher_encrypt_inline(gc_cipher_handle handle, + size_t len, + char *data) +{ + if (gcry_cipher_encrypt((gcry_cipher_hd_t) handle, data, len, NULL, len) != 0) + return GC_INVALID_CIPHER; + + return GC_OK; +} + +Gc_rc gc_cipher_decrypt_inline(gc_cipher_handle handle, + size_t len, + char *data) +{ + if (gcry_cipher_decrypt((gcry_cipher_hd_t) handle, data, len, NULL, len) != 0) + return GC_INVALID_CIPHER; + + return GC_OK; +} + +Gc_rc gc_cipher_close(gc_cipher_handle handle) +{ + gcry_cipher_close(handle); + + return GC_OK; +} + +/* Hashes. */ + +typedef struct _gc_hash_ctx + { + Gc_hash alg; + Gc_hash_mode mode; + gcry_md_hd_t gch; + } _gc_hash_ctx; + +Gc_rc gc_hash_open(Gc_hash hash, + Gc_hash_mode mode, + gc_hash_handle * outhandle) +{ + _gc_hash_ctx *ctx; + int gcryalg = 0, gcrymode = 0; + gcry_error_t err; + Gc_rc rc = GC_OK; + + ctx = calloc(sizeof (*ctx), 1); + if (!ctx) + return GC_MALLOC_ERROR; + + ctx->alg = hash; + ctx->mode = mode; + + switch (hash) + { + case GC_MD2: + gcryalg = GCRY_MD_NONE; + break; + + case GC_MD4: + gcryalg = GCRY_MD_MD4; + break; + + case GC_MD5: + gcryalg = GCRY_MD_MD5; + break; + + case GC_SHA1: + gcryalg = GCRY_MD_SHA1; + break; + + case GC_SHA256: + gcryalg = GCRY_MD_SHA256; + break; + + case GC_SHA384: + gcryalg = GCRY_MD_SHA384; + break; + + case GC_SHA512: + gcryalg = GCRY_MD_SHA512; + break; + + case GC_RMD160: + gcryalg = GCRY_MD_RMD160; + break; + + default: + rc = GC_INVALID_HASH; + } + + switch (mode) + { + case 0: + gcrymode = 0; + break; + + case GC_HMAC: + gcrymode = GCRY_MD_FLAG_HMAC; + break; + + default: + rc = GC_INVALID_HASH; + } + + if (rc == GC_OK && gcryalg != GCRY_MD_NONE) + { + err = gcry_md_open(&ctx->gch, gcryalg, gcrymode); + if (gcry_err_code(err)) + rc = GC_INVALID_HASH; + } + + if (rc == GC_OK) + *outhandle = ctx; + else + free(ctx); + + return rc; +} + +Gc_rc gc_hash_clone(gc_hash_handle handle, + gc_hash_handle * outhandle) +{ + _gc_hash_ctx *in = handle; + _gc_hash_ctx *out; + int err; + + *outhandle = out = calloc(sizeof (*out), 1); + if (!out) + return GC_MALLOC_ERROR; + + memcpy(out, in, sizeof (*out)); + + err = gcry_md_copy(&out->gch, in->gch); + if (err) + { + free(out); + return GC_INVALID_HASH; + } + + return GC_OK; +} + +size_t gc_hash_digest_length(Gc_hash hash) +{ + size_t len; + + switch (hash) + { + case GC_MD2: + len = GC_MD2_DIGEST_SIZE; + break; + + case GC_MD4: + len = GC_MD4_DIGEST_SIZE; + break; + + case GC_MD5: + len = GC_MD5_DIGEST_SIZE; + break; + + case GC_RMD160: + len = GC_RMD160_DIGEST_SIZE; + break; + + case GC_SHA1: + len = GC_SHA1_DIGEST_SIZE; + break; + + case GC_SHA256: + len = GC_SHA256_DIGEST_SIZE; + break; + + case GC_SHA384: + len = GC_SHA384_DIGEST_SIZE; + break; + + case GC_SHA512: + len = GC_SHA512_DIGEST_SIZE; + break; + + default: + return 0; + } + + return len; +} + +void gc_hash_hmac_setkey(gc_hash_handle handle, + size_t len, + const char *key) +{ + _gc_hash_ctx *ctx = handle; + gcry_md_setkey(ctx->gch, key, len); +} + +void gc_hash_write(gc_hash_handle handle, + size_t len, + const char *data) +{ + _gc_hash_ctx *ctx = handle; + gcry_md_write(ctx->gch, data, len); +} + +const char * gc_hash_read(gc_hash_handle handle) +{ + _gc_hash_ctx *ctx = handle; + const char *digest; + { + gcry_md_final (ctx->gch); + digest = gcry_md_read(ctx->gch, 0); + } + + return digest; +} + +void gc_hash_close(gc_hash_handle handle) +{ + _gc_hash_ctx *ctx = handle; + + gcry_md_close(ctx->gch); + + free(ctx); +} + +Gc_rc gc_hash_buffer(Gc_hash hash, + const void *in, + size_t inlen, + char *resbuf) +{ + int gcryalg; + + switch (hash) + { +#ifdef GNULIB_GC_MD5 + case GC_MD5: + gcryalg = GCRY_MD_MD5; + break; +#endif + +#ifdef GNULIB_GC_SHA1 + case GC_SHA1: + gcryalg = GCRY_MD_SHA1; + break; +#endif + +#ifdef GNULIB_GC_SHA256 + case GC_SHA256: + gcryalg = GCRY_MD_SHA256; + break; +#endif + +#ifdef GNULIB_GC_SHA384 + case GC_SHA384: + gcryalg = GCRY_MD_SHA384; + break; +#endif + +#ifdef GNULIB_GC_SHA512 + case GC_SHA512: + gcryalg = GCRY_MD_SHA512; + break; +#endif + +#ifdef GNULIB_GC_RMD160 + case GC_RMD160: + gcryalg = GCRY_MD_RMD160; + break; +#endif + + default: + return GC_INVALID_HASH; + } + + gcry_md_hash_buffer(gcryalg, resbuf, in, inlen); + + return GC_OK; +} + +/* One-call interface. */ +#ifdef GNULIB_GC_MD5 +Gc_rc +gc_md5 (const void *in, size_t inlen, void *resbuf) + { + size_t outlen = gcry_md_get_algo_dlen (GCRY_MD_MD5); + gcry_md_hd_t hd; + gpg_error_t err; + unsigned char *p; + + assert (outlen == GC_MD5_DIGEST_SIZE); + + err = gcry_md_open (&hd, GCRY_MD_MD5, 0); + if (err != GPG_ERR_NO_ERROR) + return GC_INVALID_HASH; + + gcry_md_write (hd, in, inlen); + + p = gcry_md_read (hd, GCRY_MD_MD5); + if (p == NULL) + { + gcry_md_close (hd); + return GC_INVALID_HASH; + } + + memcpy (resbuf, p, outlen); + + gcry_md_close (hd); + + return GC_OK; + } +#endif + +#ifdef GNULIB_GC_SHA1 +Gc_rc +gc_sha1 (const void *in, size_t inlen, void *resbuf) + { + size_t outlen = gcry_md_get_algo_dlen (GCRY_MD_SHA1); + gcry_md_hd_t hd; + gpg_error_t err; + unsigned char *p; + + assert (outlen == GC_SHA1_DIGEST_SIZE); + + err = gcry_md_open (&hd, GCRY_MD_SHA1, 0); + if (err != GPG_ERR_NO_ERROR) + return GC_INVALID_HASH; + + gcry_md_write (hd, in, inlen); + + p = gcry_md_read (hd, GCRY_MD_SHA1); + if (p == NULL) + { + gcry_md_close (hd); + return GC_INVALID_HASH; + } + + memcpy (resbuf, p, outlen); + + gcry_md_close (hd); + + return GC_OK; + } +#endif + +#ifdef GNULIB_GC_HMAC_MD5 +Gc_rc +gc_hmac_md5 (const void *key, size_t keylen, + const void *in, size_t inlen, char *resbuf) + { + size_t hlen = gcry_md_get_algo_dlen (GCRY_MD_MD5); + gcry_md_hd_t mdh; + unsigned char *hash; + gpg_error_t err; + + assert (hlen == 16); + + err = gcry_md_open (&mdh, GCRY_MD_MD5, GCRY_MD_FLAG_HMAC); + if (err != GPG_ERR_NO_ERROR) + return GC_INVALID_HASH; + + err = gcry_md_setkey (mdh, key, keylen); + if (err != GPG_ERR_NO_ERROR) + { + gcry_md_close (mdh); + return GC_INVALID_HASH; + } + + gcry_md_write (mdh, in, inlen); + + hash = gcry_md_read (mdh, GCRY_MD_MD5); + if (hash == NULL) + { + gcry_md_close (mdh); + return GC_INVALID_HASH; + } + + memcpy (resbuf, hash, hlen); + + gcry_md_close (mdh); + + return GC_OK; + } +#endif + +#ifdef GNULIB_GC_HMAC_SHA1 +Gc_rc gc_hmac_sha1(const void *key, + size_t keylen, + const void *in, + size_t inlen, + char *resbuf) +{ + size_t hlen = gcry_md_get_algo_dlen(GCRY_MD_SHA1); + gcry_md_hd_t mdh; + unsigned char *hash; + gpg_error_t err; + + assert (hlen == GC_SHA1_DIGEST_SIZE); + + err = gcry_md_open(&mdh, GCRY_MD_SHA1, GCRY_MD_FLAG_HMAC); + if (err != GPG_ERR_NO_ERROR) + return GC_INVALID_HASH; + + err = gcry_md_setkey(mdh, key, keylen); + if (err != GPG_ERR_NO_ERROR) + { + gcry_md_close(mdh); + return GC_INVALID_HASH; + } + + gcry_md_write(mdh, in, inlen); + + hash = gcry_md_read(mdh, GCRY_MD_SHA1); + if (hash == NULL) + { + gcry_md_close(mdh); + return GC_INVALID_HASH; + } + + memcpy(resbuf, hash, hlen); + + gcry_md_close(mdh); + + return GC_OK; +} +#endif diff --git a/src/daemon/https/lgl/gc-pbkdf2-sha1.c b/src/daemon/https/lgl/gc-pbkdf2-sha1.c new file mode 100644 index 00000000..f5daf7b7 --- /dev/null +++ b/src/daemon/https/lgl/gc-pbkdf2-sha1.c @@ -0,0 +1,185 @@ +/* gc-pbkdf2-sha1.c --- Password-Based Key Derivation Function a'la PKCS#5 + Copyright (C) 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1, or (at your option) + any later version. + + This program 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Written by Simon Josefsson. The comments in this file are taken + from RFC 2898. */ + +#include + +#include "gc.h" + +#include +#include + +/* + * 5.2 PBKDF2 + * + * PBKDF2 applies a pseudorandom function (see Appendix B.1 for an + * example) to derive keys. The length of the derived key is essentially + * unbounded. (However, the maximum effective search space for the + * derived key may be limited by the structure of the underlying + * pseudorandom function. See Appendix B.1 for further discussion.) + * PBKDF2 is recommended for new applications. + * + * PBKDF2 (P, S, c, dkLen) + * + * Options: PRF underlying pseudorandom function (hLen + * denotes the length in octets of the + * pseudorandom function output) + * + * Input: P password, an octet string (ASCII or UTF-8) + * S salt, an octet string + * c iteration count, a positive integer + * dkLen intended length in octets of the derived + * key, a positive integer, at most + * (2^32 - 1) * hLen + * + * Output: DK derived key, a dkLen-octet string + */ + +Gc_rc +gc_pbkdf2_sha1 (const char *P, size_t Plen, + const char *S, size_t Slen, + unsigned int c, char *DK, size_t dkLen) +{ + unsigned int hLen = 20; + char U[20]; + char T[20]; + unsigned int u; + unsigned int l; + unsigned int r; + unsigned int i; + unsigned int k; + int rc; + char *tmp; + size_t tmplen = Slen + 4; + + if (c == 0) + return GC_PKCS5_INVALID_ITERATION_COUNT; + + if (dkLen == 0) + return GC_PKCS5_INVALID_DERIVED_KEY_LENGTH; + + /* + * + * Steps: + * + * 1. If dkLen > (2^32 - 1) * hLen, output "derived key too long" and + * stop. + */ + + if (dkLen > 4294967295U) + return GC_PKCS5_DERIVED_KEY_TOO_LONG; + + /* + * 2. Let l be the number of hLen-octet blocks in the derived key, + * rounding up, and let r be the number of octets in the last + * block: + * + * l = CEIL (dkLen / hLen) , + * r = dkLen - (l - 1) * hLen . + * + * Here, CEIL (x) is the "ceiling" function, i.e. the smallest + * integer greater than, or equal to, x. + */ + + l = ((dkLen - 1) / hLen) + 1; + r = dkLen - (l - 1) * hLen; + + /* + * 3. For each block of the derived key apply the function F defined + * below to the password P, the salt S, the iteration count c, and + * the block index to compute the block: + * + * T_1 = F (P, S, c, 1) , + * T_2 = F (P, S, c, 2) , + * ... + * T_l = F (P, S, c, l) , + * + * where the function F is defined as the exclusive-or sum of the + * first c iterates of the underlying pseudorandom function PRF + * applied to the password P and the concatenation of the salt S + * and the block index i: + * + * F (P, S, c, i) = U_1 \xor U_2 \xor ... \xor U_c + * + * where + * + * U_1 = PRF (P, S || INT (i)) , + * U_2 = PRF (P, U_1) , + * ... + * U_c = PRF (P, U_{c-1}) . + * + * Here, INT (i) is a four-octet encoding of the integer i, most + * significant octet first. + * + * 4. Concatenate the blocks and extract the first dkLen octets to + * produce a derived key DK: + * + * DK = T_1 || T_2 || ... || T_l<0..r-1> + * + * 5. Output the derived key DK. + * + * Note. The construction of the function F follows a "belt-and- + * suspenders" approach. The iterates U_i are computed recursively to + * remove a degree of parallelism from an opponent; they are exclusive- + * ored together to reduce concerns about the recursion degenerating + * into a small set of values. + * + */ + + tmp = malloc (tmplen); + if (tmp == NULL) + return GC_MALLOC_ERROR; + + memcpy (tmp, S, Slen); + + for (i = 1; i <= l; i++) + { + memset (T, 0, hLen); + + for (u = 1; u <= c; u++) + { + if (u == 1) + { + tmp[Slen + 0] = (i & 0xff000000) >> 24; + tmp[Slen + 1] = (i & 0x00ff0000) >> 16; + tmp[Slen + 2] = (i & 0x0000ff00) >> 8; + tmp[Slen + 3] = (i & 0x000000ff) >> 0; + + rc = gc_hmac_sha1 (P, Plen, tmp, tmplen, U); + } + else + rc = gc_hmac_sha1 (P, Plen, U, hLen, U); + + if (rc != GC_OK) + { + free (tmp); + return rc; + } + + for (k = 0; k < hLen; k++) + T[k] ^= U[k]; + } + + memcpy (DK + (i - 1) * hLen, T, i == l ? r : hLen); + } + + free (tmp); + + return GC_OK; +} diff --git a/src/daemon/https/lgl/gc.h b/src/daemon/https/lgl/gc.h new file mode 100644 index 00000000..688e624a --- /dev/null +++ b/src/daemon/https/lgl/gc.h @@ -0,0 +1,347 @@ +/* gc.h --- Header file for implementation agnostic crypto wrapper API. + * Copyright (C) 2002, 2003, 2004, 2005, 2007 Simon Josefsson + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2.1, or (at your + * option) any later version. + * + * This file 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 Lesser General Public License + * along with this file; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +#ifndef GC_H +#define GC_H + +/* Get size_t. */ +# include + +enum Gc_rc + { + GC_OK = 0, + GC_MALLOC_ERROR, + GC_INIT_ERROR, + GC_RANDOM_ERROR, + GC_INVALID_CIPHER, + GC_INVALID_HASH, + GC_PKCS5_INVALID_ITERATION_COUNT, + GC_PKCS5_INVALID_DERIVED_KEY_LENGTH, + GC_PKCS5_DERIVED_KEY_TOO_LONG + }; +typedef enum Gc_rc Gc_rc; + +/* Hash types. */ +enum Gc_hash + { + GC_MD4, + GC_MD5, + GC_SHA1, + GC_MD2, + GC_RMD160, + GC_SHA256, + GC_SHA384, + GC_SHA512 + }; +typedef enum Gc_hash Gc_hash; + +enum Gc_hash_mode + { + GC_HMAC = 1 + }; +typedef enum Gc_hash_mode Gc_hash_mode; + +typedef void *gc_hash_handle; + +#define GC_MD2_DIGEST_SIZE 16 +#define GC_MD4_DIGEST_SIZE 16 +#define GC_MD5_DIGEST_SIZE 16 +#define GC_RMD160_DIGEST_SIZE 20 +#define GC_SHA1_DIGEST_SIZE 20 +#define GC_SHA256_DIGEST_SIZE 32 +#define GC_SHA384_DIGEST_SIZE 48 +#define GC_SHA512_DIGEST_SIZE 64 + +/* Cipher types. */ +enum Gc_cipher + { + GC_AES128, + GC_AES192, + GC_AES256, + GC_3DES, + GC_DES, + GC_ARCFOUR128, + GC_ARCFOUR40, + GC_ARCTWO40, + GC_CAMELLIA128, + GC_CAMELLIA256 + }; +typedef enum Gc_cipher Gc_cipher; + +enum Gc_cipher_mode + { + GC_ECB, + GC_CBC, + GC_STREAM + }; +typedef enum Gc_cipher_mode Gc_cipher_mode; + +typedef void * gc_cipher_handle; + +/* Call before respectively after any other functions. */ +Gc_rc gc_init(void); +void gc_done(void); + +/* Memory allocation (avoid). */ +typedef void *(*gc_malloc_t)(size_t n); +typedef int (*gc_secure_check_t)(const void *); +typedef void *(*gc_realloc_t)(void *p, + size_t n); +typedef void (*gc_free_t)(void *); +void gc_set_allocators(gc_malloc_t func_malloc, + gc_malloc_t secure_malloc, + gc_secure_check_t secure_check, + gc_realloc_t func_realloc, + gc_free_t func_free); + +/* Randomness. */ +Gc_rc gc_nonce(char *data, + size_t datalen); +Gc_rc gc_pseudo_random(char *data, + size_t datalen); +Gc_rc gc_random(char *data, + size_t datalen); + +/* Ciphers. */ +Gc_rc gc_cipher_open(Gc_cipher cipher, + Gc_cipher_mode mode, + gc_cipher_handle *outhandle); +Gc_rc gc_cipher_setkey(gc_cipher_handle handle, + size_t keylen, + const char *key); +Gc_rc gc_cipher_setiv(gc_cipher_handle handle, + size_t ivlen, + const char *iv); +Gc_rc gc_cipher_encrypt_inline(gc_cipher_handle handle, + size_t len, + char *data); +Gc_rc gc_cipher_decrypt_inline(gc_cipher_handle handle, + size_t len, + char *data); +Gc_rc gc_cipher_close(gc_cipher_handle handle); + +/* Hashes. */ + +Gc_rc gc_hash_open(Gc_hash hash, + Gc_hash_mode mode, + gc_hash_handle *outhandle); +Gc_rc gc_hash_clone(gc_hash_handle handle, + gc_hash_handle *outhandle); +size_t gc_hash_digest_length(Gc_hash hash); +void gc_hash_hmac_setkey(gc_hash_handle handle, + size_t len, + const char *key); +void gc_hash_write(gc_hash_handle handle, + size_t len, + const char *data); +const char *gc_hash_read(gc_hash_handle handle); +void gc_hash_close(gc_hash_handle handle); + +/* Compute a hash value over buffer IN of INLEN bytes size using the + algorithm HASH, placing the result in the pre-allocated buffer OUT. + The required size of OUT depends on HASH, and is generally + GC__DIGEST_SIZE. For example, for GC_MD5 the output buffer + must be 16 bytes. The return value is 0 (GC_OK) on success, or + another Gc_rc error code. */ +Gc_rc gc_hash_buffer(Gc_hash hash, + const void *in, + size_t inlen, + char *out); + +/* One-call interface. */ +Gc_rc gc_md2(const void *in, + size_t inlen, + void *resbuf); +Gc_rc gc_md4(const void *in, + size_t inlen, + void *resbuf); +Gc_rc gc_md5(const void *in, + size_t inlen, + void *resbuf); +Gc_rc gc_sha1(const void *in, + size_t inlen, + void *resbuf); +Gc_rc gc_hmac_md5(const void *key, + size_t keylen, + const void *in, + size_t inlen, + char *resbuf); +Gc_rc gc_hmac_sha1(const void *key, + size_t keylen, + const void *in, + size_t inlen, + char *resbuf); + +/* Derive cryptographic keys from a password P of length PLEN, with + salt S of length SLEN, placing the result in pre-allocated buffer + DK of length DKLEN. An iteration count is specified in C, where a + larger value means this function take more time (typical iteration + counts are 1000-20000). This function "stretches" the key to be + exactly dkLen bytes long. GC_OK is returned on success, otherwise + an Gc_rc error code is returned. */ +Gc_rc gc_pbkdf2_sha1(const char *P, + size_t Plen, + const char *S, + size_t Slen, + unsigned int c, + char *DK, + size_t dkLen); + +/* + TODO: + + From: Simon Josefsson + Subject: Re: generic crypto + Newsgroups: gmane.comp.lib.gnulib.bugs + Cc: bug-gnulib@gnu.org + Date: Fri, 07 Oct 2005 12:50:57 +0200 + Mail-Copies-To: nobody + + Paul Eggert writes: + + > Simon Josefsson writes: + > + >> * Perhaps the /dev/?random reading should be separated into a separate + >> module? It might be useful outside of the gc layer too. + > + > Absolutely. I've been meaning to do that for months (for a "shuffle" + > program I want to add to coreutils), but hadn't gotten around to it. + > It would have to be generalized a bit. I'd like to have the file + > descriptor cached, for example. + + I'll write a separate module for that part. + + I think we should even add a good PRNG that is re-seeded from + /dev/?random frequently. GnuTLS can need a lot of random data on a + big server, more than /dev/random can supply. And /dev/urandom might + not be strong enough. Further, the security of /dev/?random can also + be questionable. + + >> I'm also not sure about the names of those functions, they suggest + >> a more higher-level API than what is really offered (i.e., the + >> names "nonce" and "pseudo_random" and "random" imply certain + >> cryptographic properties). + > + > Could you expand a bit more on that? What is the relationship between + > nonce/pseudorandom/random and the /dev/ values you are using? + + There is none, that is the problem. + + Applications generally need different kind of "random" numbers. + Sometimes they just need some random data and doesn't care whether it + is possible for an attacker to compute the string (aka a "nonce"). + Sometimes they need data that is very difficult to compute (i.e., + computing it require inverting SHA1 or similar). Sometimes they need + data that is not possible to compute, i.e., it wants real entropy + collected over time on the system. Collecting the last kind of random + data is very expensive, so it must not be used too often. The second + kind of random data ("pseudo random") is typically generated by + seeding a good PRNG with a couple of hundred bytes of real entropy + from the "real random" data pool. The "nonce" is usually computed + using the PRNG as well, because PRNGs are usually fast. + + Pseudo-random data is typically used for session keys. Strong random + data is often used to generate long-term keys (e.g., private RSA + keys). + + Of course, there are many subtleties. There are several different + kind of nonce:s. Sometimes a nonce is just an ever-increasing + integer, starting from 0. Sometimes it is assumed to be unlikely to + be the same as previous nonces, but without a requirement that the + nonce is possible to guess. MD5(system clock) would thus suffice, if + it isn't called too often. You can guess what the next value will be, + but it will always be different. + + The problem is that /dev/?random doesn't offer any kind of semantic + guarantees. But applications need an API that make that promise. + + I think we should do this in several steps: + + 1) Write a module that can read from /dev/?random. + + 2) Add a module for a known-good PRNG suitable for random number + generation, that can be continuously re-seeded. + + 3) Add a high-level module that provide various different randomness + functions. One for nonces, perhaps even different kind of nonces, + one for pseudo random data, and one for strong random data. It is + not clear whether we can hope to achieve the last one in a portable + way. + + Further, it would be useful to allow users to provide their own + entropy source as a file, used to seed the PRNG or initialize the + strong randomness pool. This is used on embedded platforms that + doesn't have enough interrupts to hope to generate good random data. + + > For example, why not use OpenBSD's /dev/arandom? + + I don't trust ARC4. For example, recent cryptographic efforts + indicate that you must throw away the first 512 bytes generated from + the PRNG for it to be secure. I don't know whether OpenBSD do this. + Further, I recall some eprint paper on RC4 security that didn't + inspire confidence. + + While I trust the random devices in OpenBSD more than + Solaris/AIX/HPUX/etc, I think that since we need something better on + Solaris/AIX/HPUX we'd might as well use it on OpenBSD or even Linux + too. + + > Here is one thought. The user could specify a desired quality level + > range, and the implementation then would supply random data that is at + > least as good as the lower bound of the range. I.e., ihe + > implementation refuses to produce any random data if it can't generate + > data that is at least as good as the lower end of the range. The + > upper bound of the range is advice from the user not to be any more + > expensive than that, but the implementation can ignore the advice if + > it doesn't have anything cheaper. + + I'm not sure this is a good idea. Users can't really be expected to + understand this. Further, applications need many different kind of + random data. Selecting the randomness level for each by the user will + be too complicated. + + I think it is better if the application decide, from its cryptographic + requirement, what entropy quality it require, and call the proper API. + Meeting the implied semantic properties should be the job for gnulib. + + >> Perhaps gc_dev_random and gc_dev_urandom? + > + > To some extent. I'd rather insulate the user from the details of + > where the random numbers come from. On the other hand we need to + > provide a way for applications to specify a file that contains + > random bits, so that people can override the defaults. + + Agreed. + + This may require some thinking before it is finalized. Is it ok to + install the GC module as-is meanwhile? Then I can continue to add the + stuff that GnuTLS need, and then come back to re-working the + randomness module. That way, we have two different projects that use + the code. GnuTLS includes the same randomness code that was in GNU + SASL and that is in the current gc module. I feel much more + comfortable working in small steps at a time, rather then working on + this for a long time in gnulib and only later integrate the stuff in + GnuTLS. + + Thanks, + Simon + */ + +#endif /* GC_H */ diff --git a/src/daemon/https/lgl/gettext.h b/src/daemon/https/lgl/gettext.h new file mode 100644 index 00000000..bd214d5c --- /dev/null +++ b/src/daemon/https/lgl/gettext.h @@ -0,0 +1,270 @@ +/* Convenience header for conditional use of GNU . + Copyright (C) 1995-1998, 2000-2002, 2004-2006 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1, or (at your option) + any later version. + + This program 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef _LIBGETTEXT_H +#define _LIBGETTEXT_H 1 + +/* NLS can be disabled through the configure --disable-nls option. */ +#if ENABLE_NLS + +/* Get declarations of GNU message catalog functions. */ +# include + +/* You can set the DEFAULT_TEXT_DOMAIN macro to specify the domain used by + the gettext() and ngettext() macros. This is an alternative to calling + textdomain(), and is useful for libraries. */ +# ifdef DEFAULT_TEXT_DOMAIN +# undef gettext +# define gettext(Msgid) \ + dgettext (DEFAULT_TEXT_DOMAIN, Msgid) +# undef ngettext +# define ngettext(Msgid1, Msgid2, N) \ + dngettext (DEFAULT_TEXT_DOMAIN, Msgid1, Msgid2, N) +# endif + +#else + +/* Solaris /usr/include/locale.h includes /usr/include/libintl.h, which + chokes if dcgettext is defined as a macro. So include it now, to make + later inclusions of a NOP. We don't include + as well because people using "gettext.h" will not include , + and also including would fail on SunOS 4, whereas + is OK. */ +#if defined(__sun) +# include +#endif + +/* Many header files from the libstdc++ coming with g++ 3.3 or newer include + , which chokes if dcgettext is defined as a macro. So include + it now, to make later inclusions of a NOP. */ +#if defined(__cplusplus) && defined(__GNUG__) && (__GNUC__ >= 3) +# include +# if (__GLIBC__ >= 2) || _GLIBCXX_HAVE_LIBINTL_H +# include +# endif +#endif + +/* Disabled NLS. + The casts to 'const char *' serve the purpose of producing warnings + for invalid uses of the value returned from these functions. + On pre-ANSI systems without 'const', the config.h file is supposed to + contain "#define const". */ +# define gettext(Msgid) ((const char *) (Msgid)) +# define dgettext(Domainname, Msgid) ((void) (Domainname), gettext (Msgid)) +# define dcgettext(Domainname, Msgid, Category) \ + ((void) (Category), dgettext (Domainname, Msgid)) +# define ngettext(Msgid1, Msgid2, N) \ + ((N) == 1 \ + ? ((void) (Msgid2), (const char *) (Msgid1)) \ + : ((void) (Msgid1), (const char *) (Msgid2))) +# define dngettext(Domainname, Msgid1, Msgid2, N) \ + ((void) (Domainname), ngettext (Msgid1, Msgid2, N)) +# define dcngettext(Domainname, Msgid1, Msgid2, N, Category) \ + ((void) (Category), dngettext(Domainname, Msgid1, Msgid2, N)) +# define textdomain(Domainname) ((const char *) (Domainname)) +# define bindtextdomain(Domainname, Dirname) \ + ((void) (Domainname), (const char *) (Dirname)) +# define bind_textdomain_codeset(Domainname, Codeset) \ + ((void) (Domainname), (const char *) (Codeset)) + +#endif + +/* A pseudo function call that serves as a marker for the automated + extraction of messages, but does not call gettext(). The run-time + translation is done at a different place in the code. + The argument, String, should be a literal string. Concatenated strings + and other string expressions won't work. + The macro's expansion is not parenthesized, so that it is suitable as + initializer for static 'char[]' or 'const char[]' variables. */ +#define gettext_noop(String) String + +/* The separator between msgctxt and msgid in a .mo file. */ +#define GETTEXT_CONTEXT_GLUE "\004" + +/* Pseudo function calls, taking a MSGCTXT and a MSGID instead of just a + MSGID. MSGCTXT and MSGID must be string literals. MSGCTXT should be + short and rarely need to change. + The letter 'p' stands for 'particular' or 'special'. */ +#ifdef DEFAULT_TEXT_DOMAIN +# define pgettext(Msgctxt, Msgid) \ + pgettext_aux (DEFAULT_TEXT_DOMAIN, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, LC_MESSAGES) +#else +# define pgettext(Msgctxt, Msgid) \ + pgettext_aux (NULL, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, LC_MESSAGES) +#endif +#define dpgettext(Domainname, Msgctxt, Msgid) \ + pgettext_aux (Domainname, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, LC_MESSAGES) +#define dcpgettext(Domainname, Msgctxt, Msgid, Category) \ + pgettext_aux (Domainname, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, Category) +#ifdef DEFAULT_TEXT_DOMAIN +# define npgettext(Msgctxt, Msgid, MsgidPlural, N) \ + npgettext_aux (DEFAULT_TEXT_DOMAIN, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, MsgidPlural, N, LC_MESSAGES) +#else +# define npgettext(Msgctxt, Msgid, MsgidPlural, N) \ + npgettext_aux (NULL, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, MsgidPlural, N, LC_MESSAGES) +#endif +#define dnpgettext(Domainname, Msgctxt, Msgid, MsgidPlural, N) \ + npgettext_aux (Domainname, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, MsgidPlural, N, LC_MESSAGES) +#define dcnpgettext(Domainname, Msgctxt, Msgid, MsgidPlural, N, Category) \ + npgettext_aux (Domainname, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, MsgidPlural, N, Category) + +#ifdef __GNUC__ +__inline +#else +#ifdef __cplusplus +inline +#endif +#endif +static const char * +pgettext_aux (const char *domain, + const char *msg_ctxt_id, const char *msgid, + int category) +{ + const char *translation = dcgettext (domain, msg_ctxt_id, category); + if (translation == msg_ctxt_id) + return msgid; + else + return translation; +} + +#ifdef __GNUC__ +__inline +#else +#ifdef __cplusplus +inline +#endif +#endif +static const char * +npgettext_aux (const char *domain, + const char *msg_ctxt_id, const char *msgid, + const char *msgid_plural, unsigned long int n, + int category) +{ + const char *translation = + dcngettext (domain, msg_ctxt_id, msgid_plural, n, category); + if (translation == msg_ctxt_id || translation == msgid_plural) + return (n == 1 ? msgid : msgid_plural); + else + return translation; +} + +/* The same thing extended for non-constant arguments. Here MSGCTXT and MSGID + can be arbitrary expressions. But for string literals these macros are + less efficient than those above. */ + +#include + +#define _LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS \ + (((__GNUC__ >= 3 || __GNUG__ >= 2) && !__STRICT_ANSI__) \ + /* || __STDC_VERSION__ >= 199901L */ ) + +#if !_LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS +#include +#endif + +#define pgettext_expr(Msgctxt, Msgid) \ + dcpgettext_expr (NULL, Msgctxt, Msgid, LC_MESSAGES) +#define dpgettext_expr(Domainname, Msgctxt, Msgid) \ + dcpgettext_expr (Domainname, Msgctxt, Msgid, LC_MESSAGES) + +#ifdef __GNUC__ +__inline +#else +#ifdef __cplusplus +inline +#endif +#endif +static const char * +dcpgettext_expr (const char *domain, + const char *msgctxt, const char *msgid, + int category) +{ + size_t msgctxt_len = strlen (msgctxt) + 1; + size_t msgid_len = strlen (msgid) + 1; + const char *translation; +#if _LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS + char msg_ctxt_id[msgctxt_len + msgid_len]; +#else + char buf[1024]; + char *msg_ctxt_id = + (msgctxt_len + msgid_len <= sizeof (buf) + ? buf + : (char *) malloc (msgctxt_len + msgid_len)); + if (msg_ctxt_id != NULL) +#endif + { + memcpy (msg_ctxt_id, msgctxt, msgctxt_len - 1); + msg_ctxt_id[msgctxt_len - 1] = '\004'; + memcpy (msg_ctxt_id + msgctxt_len, msgid, msgid_len); + translation = dcgettext (domain, msg_ctxt_id, category); +#if !_LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS + if (msg_ctxt_id != buf) + free (msg_ctxt_id); +#endif + if (translation != msg_ctxt_id) + return translation; + } + return msgid; +} + +#define npgettext_expr(Msgctxt, Msgid, MsgidPlural, N) \ + dcnpgettext_expr (NULL, Msgctxt, Msgid, MsgidPlural, N, LC_MESSAGES) +#define dnpgettext_expr(Domainname, Msgctxt, Msgid, MsgidPlural, N) \ + dcnpgettext_expr (Domainname, Msgctxt, Msgid, MsgidPlural, N, LC_MESSAGES) + +#ifdef __GNUC__ +__inline +#else +#ifdef __cplusplus +inline +#endif +#endif +static const char * +dcnpgettext_expr (const char *domain, + const char *msgctxt, const char *msgid, + const char *msgid_plural, unsigned long int n, + int category) +{ + size_t msgctxt_len = strlen (msgctxt) + 1; + size_t msgid_len = strlen (msgid) + 1; + const char *translation; +#if _LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS + char msg_ctxt_id[msgctxt_len + msgid_len]; +#else + char buf[1024]; + char *msg_ctxt_id = + (msgctxt_len + msgid_len <= sizeof (buf) + ? buf + : (char *) malloc (msgctxt_len + msgid_len)); + if (msg_ctxt_id != NULL) +#endif + { + memcpy (msg_ctxt_id, msgctxt, msgctxt_len - 1); + msg_ctxt_id[msgctxt_len - 1] = '\004'; + memcpy (msg_ctxt_id + msgctxt_len, msgid, msgid_len); + translation = dcngettext (domain, msg_ctxt_id, msgid_plural, n, category); +#if !_LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS + if (msg_ctxt_id != buf) + free (msg_ctxt_id); +#endif + if (!(translation == msg_ctxt_id || translation == msgid_plural)) + return translation; + } + return (n == 1 ? msgid : msgid_plural); +} + +#endif /* _LIBGETTEXT_H */ diff --git a/src/daemon/https/lgl/hmac-md5.c b/src/daemon/https/lgl/hmac-md5.c new file mode 100644 index 00000000..53cf2b10 --- /dev/null +++ b/src/daemon/https/lgl/hmac-md5.c @@ -0,0 +1,81 @@ +/* hmac-md5.c -- hashed message authentication codes + Copyright (C) 2005, 2006 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1, or (at your option) + any later version. + + This program 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Written by Simon Josefsson. */ + +#include + +#include "hmac.h" + +#include "memxor.h" +#include "md5.h" + +#include + +#define IPAD 0x36 +#define OPAD 0x5c + +int +hmac_md5 (const void *key, size_t keylen, + const void *in, size_t inlen, void *resbuf) +{ + struct md5_ctx inner; + struct md5_ctx outer; + char optkeybuf[16]; + char block[64]; + char innerhash[16]; + + /* Reduce the key's size, so that it becomes <= 64 bytes large. */ + + if (keylen > 64) + { + struct md5_ctx keyhash; + + md5_init_ctx (&keyhash); + md5_process_bytes (key, keylen, &keyhash); + md5_finish_ctx (&keyhash, optkeybuf); + + key = optkeybuf; + keylen = 16; + } + + /* Compute INNERHASH from KEY and IN. */ + + md5_init_ctx (&inner); + + memset (block, IPAD, sizeof (block)); + memxor (block, key, keylen); + + md5_process_block (block, 64, &inner); + md5_process_bytes (in, inlen, &inner); + + md5_finish_ctx (&inner, innerhash); + + /* Compute result from KEY and INNERHASH. */ + + md5_init_ctx (&outer); + + memset (block, OPAD, sizeof (block)); + memxor (block, key, keylen); + + md5_process_block (block, 64, &outer); + md5_process_bytes (innerhash, 16, &outer); + + md5_finish_ctx (&outer, resbuf); + + return 0; +} diff --git a/src/daemon/https/lgl/hmac-sha1.c b/src/daemon/https/lgl/hmac-sha1.c new file mode 100644 index 00000000..a9cf18cd --- /dev/null +++ b/src/daemon/https/lgl/hmac-sha1.c @@ -0,0 +1,81 @@ +/* hmac-sha1.c -- hashed message authentication codes + Copyright (C) 2005, 2006 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1, or (at your option) + any later version. + + This program 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Written by Simon Josefsson. */ + +#include + +#include "hmac.h" + +#include "memxor.h" +#include "sha1.h" + +#include + +#define IPAD 0x36 +#define OPAD 0x5c + +int +hmac_sha1 (const void *key, size_t keylen, + const void *in, size_t inlen, void *resbuf) +{ + struct sha1_ctx inner; + struct sha1_ctx outer; + char optkeybuf[20]; + char block[64]; + char innerhash[20]; + + /* Reduce the key's size, so that it becomes <= 64 bytes large. */ + + if (keylen > 64) + { + struct sha1_ctx keyhash; + + sha1_init_ctx (&keyhash); + sha1_process_bytes (key, keylen, &keyhash); + sha1_finish_ctx (&keyhash, optkeybuf); + + key = optkeybuf; + keylen = 20; + } + + /* Compute INNERHASH from KEY and IN. */ + + sha1_init_ctx (&inner); + + memset (block, IPAD, sizeof (block)); + memxor (block, key, keylen); + + sha1_process_block (block, 64, &inner); + sha1_process_bytes (in, inlen, &inner); + + sha1_finish_ctx (&inner, innerhash); + + /* Compute result from KEY and INNERHASH. */ + + sha1_init_ctx (&outer); + + memset (block, OPAD, sizeof (block)); + memxor (block, key, keylen); + + sha1_process_block (block, 64, &outer); + sha1_process_bytes (innerhash, 20, &outer); + + sha1_finish_ctx (&outer, resbuf); + + return 0; +} diff --git a/src/daemon/https/lgl/hmac.h b/src/daemon/https/lgl/hmac.h new file mode 100644 index 00000000..5965b603 --- /dev/null +++ b/src/daemon/https/lgl/hmac.h @@ -0,0 +1,41 @@ +/* hmac.h -- hashed message authentication codes + Copyright (C) 2005 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1, or (at your option) + any later version. + + This program 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Written by Simon Josefsson. */ + +#ifndef HMAC_H +# define HMAC_H 1 + +#include + +/* Compute Hashed Message Authentication Code with MD5, as described + in RFC 2104, over BUFFER data of BUFLEN bytes using the KEY of + KEYLEN bytes, writing the output to pre-allocated 16 byte minimum + RESBUF buffer. Return 0 on success. */ +int +hmac_md5 (const void *key, size_t keylen, + const void *buffer, size_t buflen, void *resbuf); + +/* Compute Hashed Message Authentication Code with SHA-1, over BUFFER + data of BUFLEN bytes using the KEY of KEYLEN bytes, writing the + output to pre-allocated 20 byte minimum RESBUF buffer. Return 0 on + success. */ +int +hmac_sha1 (const void *key, size_t keylen, + const void *in, size_t inlen, void *resbuf); + +#endif /* HMAC_H */ diff --git a/src/daemon/https/lgl/md5.c b/src/daemon/https/lgl/md5.c new file mode 100644 index 00000000..9b3bfbe8 --- /dev/null +++ b/src/daemon/https/lgl/md5.c @@ -0,0 +1,451 @@ +/* Functions to compute MD5 message digest of files or memory blocks. + according to the definition of MD5 in RFC 1321 from April 1992. + Copyright (C) 1995,1996,1997,1999,2000,2001,2005,2006 + Free Software Foundation, Inc. + This file is part of the GNU C Library. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by the + Free Software Foundation; either version 2.1, or (at your option) any + later version. + + This program 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Written by Ulrich Drepper , 1995. */ + +#include + +#include "md5.h" + +#include +#include +#include +#include + +#if USE_UNLOCKED_IO +# include "unlocked-io.h" +#endif + +#ifdef _LIBC +# include +# if __BYTE_ORDER == __BIG_ENDIAN +# define WORDS_BIGENDIAN 1 +# endif +/* We need to keep the namespace clean so define the MD5 function + protected using leading __ . */ +# define md5_init_ctx __md5_init_ctx +# define md5_process_block __md5_process_block +# define md5_process_bytes __md5_process_bytes +# define md5_finish_ctx __md5_finish_ctx +# define md5_read_ctx __md5_read_ctx +# define md5_stream __md5_stream +# define md5_buffer __md5_buffer +#endif + +#ifdef WORDS_BIGENDIAN +# define SWAP(n) \ + (((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24)) +#else +# define SWAP(n) (n) +#endif + +#define BLOCKSIZE 4096 +#if BLOCKSIZE % 64 != 0 +# error "invalid BLOCKSIZE" +#endif + +/* This array contains the bytes used to pad the buffer to the next + 64-byte boundary. (RFC 1321, 3.1: Step 1) */ +static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ... */ }; + + +/* Initialize structure containing state of computation. + (RFC 1321, 3.3: Step 3) */ +void +md5_init_ctx (struct md5_ctx *ctx) +{ + ctx->A = 0x67452301; + ctx->B = 0xefcdab89; + ctx->C = 0x98badcfe; + ctx->D = 0x10325476; + + ctx->total[0] = ctx->total[1] = 0; + ctx->buflen = 0; +} + +/* Put result from CTX in first 16 bytes following RESBUF. The result + must be in little endian byte order. + + IMPORTANT: On some systems it is required that RESBUF is correctly + aligned for a 32-bit value. */ +void * +md5_read_ctx (const struct md5_ctx *ctx, void *resbuf) +{ + ((uint32_t *) resbuf)[0] = SWAP (ctx->A); + ((uint32_t *) resbuf)[1] = SWAP (ctx->B); + ((uint32_t *) resbuf)[2] = SWAP (ctx->C); + ((uint32_t *) resbuf)[3] = SWAP (ctx->D); + + return resbuf; +} + +/* Process the remaining bytes in the internal buffer and the usual + prolog according to the standard and write the result to RESBUF. + + IMPORTANT: On some systems it is required that RESBUF is correctly + aligned for a 32-bit value. */ +void * +md5_finish_ctx (struct md5_ctx *ctx, void *resbuf) +{ + /* Take yet unprocessed bytes into account. */ + uint32_t bytes = ctx->buflen; + size_t size = (bytes < 56) ? 64 / 4 : 64 * 2 / 4; + + /* Now count remaining bytes. */ + ctx->total[0] += bytes; + if (ctx->total[0] < bytes) + ++ctx->total[1]; + + /* Put the 64-bit file length in *bits* at the end of the buffer. */ + ctx->buffer[size - 2] = SWAP (ctx->total[0] << 3); + ctx->buffer[size - 1] = SWAP ((ctx->total[1] << 3) | (ctx->total[0] >> 29)); + + memcpy (&((char *) ctx->buffer)[bytes], fillbuf, (size - 2) * 4 - bytes); + + /* Process last bytes. */ + md5_process_block (ctx->buffer, size * 4, ctx); + + return md5_read_ctx (ctx, resbuf); +} + +/* Compute MD5 message digest for bytes read from STREAM. The + resulting message digest number will be written into the 16 bytes + beginning at RESBLOCK. */ +int +md5_stream (FILE * stream, void *resblock) +{ + struct md5_ctx ctx; + char buffer[BLOCKSIZE + 72]; + size_t sum; + + /* Initialize the computation context. */ + md5_init_ctx (&ctx); + + /* Iterate over full file contents. */ + while (1) + { + /* We read the file in blocks of BLOCKSIZE bytes. One call of the + computation function processes the whole buffer so that with the + next round of the loop another block can be read. */ + size_t n; + sum = 0; + + /* Read block. Take care for partial reads. */ + while (1) + { + n = fread (buffer + sum, 1, BLOCKSIZE - sum, stream); + + sum += n; + + if (sum == BLOCKSIZE) + break; + + if (n == 0) + { + /* Check for the error flag IFF N == 0, so that we don't + exit the loop after a partial read due to e.g., EAGAIN + or EWOULDBLOCK. */ + if (ferror (stream)) + return 1; + goto process_partial_block; + } + + /* We've read at least one byte, so ignore errors. But always + check for EOF, since feof may be true even though N > 0. + Otherwise, we could end up calling fread after EOF. */ + if (feof (stream)) + goto process_partial_block; + } + + /* Process buffer with BLOCKSIZE bytes. Note that + BLOCKSIZE % 64 == 0 + */ + md5_process_block (buffer, BLOCKSIZE, &ctx); + } + +process_partial_block: + + /* Process any remaining bytes. */ + if (sum > 0) + md5_process_bytes (buffer, sum, &ctx); + + /* Construct result in desired memory. */ + md5_finish_ctx (&ctx, resblock); + return 0; +} + +/* Compute MD5 message digest for LEN bytes beginning at BUFFER. The + result is always in little endian byte order, so that a byte-wise + output yields to the wanted ASCII representation of the message + digest. */ +void * +md5_buffer (const char *buffer, size_t len, void *resblock) +{ + struct md5_ctx ctx; + + /* Initialize the computation context. */ + md5_init_ctx (&ctx); + + /* Process whole buffer but last len % 64 bytes. */ + md5_process_bytes (buffer, len, &ctx); + + /* Put result in desired memory area. */ + return md5_finish_ctx (&ctx, resblock); +} + + +void +md5_process_bytes (const void *buffer, size_t len, struct md5_ctx *ctx) +{ + /* When we already have some bits in our internal buffer concatenate + both inputs first. */ + if (ctx->buflen != 0) + { + size_t left_over = ctx->buflen; + size_t add = 128 - left_over > len ? len : 128 - left_over; + + memcpy (&((char *) ctx->buffer)[left_over], buffer, add); + ctx->buflen += add; + + if (ctx->buflen > 64) + { + md5_process_block (ctx->buffer, ctx->buflen & ~63, ctx); + + ctx->buflen &= 63; + /* The regions in the following copy operation cannot overlap. */ + memcpy (ctx->buffer, + &((char *) ctx->buffer)[(left_over + add) & ~63], + ctx->buflen); + } + + buffer = (const char *) buffer + add; + len -= add; + } + + /* Process available complete blocks. */ + if (len >= 64) + { +#if !_STRING_ARCH_unaligned +# define alignof(type) offsetof (struct { char c; type x; }, x) +# define UNALIGNED_P(p) (((size_t) p) % alignof (uint32_t) != 0) + if (UNALIGNED_P (buffer)) + while (len > 64) + { + md5_process_block (memcpy (ctx->buffer, buffer, 64), 64, ctx); + buffer = (const char *) buffer + 64; + len -= 64; + } + else +#endif + { + md5_process_block (buffer, len & ~63, ctx); + buffer = (const char *) buffer + (len & ~63); + len &= 63; + } + } + + /* Move remaining bytes in internal buffer. */ + if (len > 0) + { + size_t left_over = ctx->buflen; + + memcpy (&((char *) ctx->buffer)[left_over], buffer, len); + left_over += len; + if (left_over >= 64) + { + md5_process_block (ctx->buffer, 64, ctx); + left_over -= 64; + memcpy (ctx->buffer, &ctx->buffer[16], left_over); + } + ctx->buflen = left_over; + } +} + + +/* These are the four functions used in the four steps of the MD5 algorithm + and defined in the RFC 1321. The first function is a little bit optimized + (as found in Colin Plumbs public domain implementation). */ +/* #define FF(b, c, d) ((b & c) | (~b & d)) */ +#define FF(b, c, d) (d ^ (b & (c ^ d))) +#define FG(b, c, d) FF (d, b, c) +#define FH(b, c, d) (b ^ c ^ d) +#define FI(b, c, d) (c ^ (b | ~d)) + +/* Process LEN bytes of BUFFER, accumulating context into CTX. + It is assumed that LEN % 64 == 0. */ + +void +md5_process_block (const void *buffer, size_t len, struct md5_ctx *ctx) +{ + uint32_t correct_words[16]; + const uint32_t *words = buffer; + size_t nwords = len / sizeof (uint32_t); + const uint32_t *endp = words + nwords; + uint32_t A = ctx->A; + uint32_t B = ctx->B; + uint32_t C = ctx->C; + uint32_t D = ctx->D; + + /* First increment the byte count. RFC 1321 specifies the possible + length of the file up to 2^64 bits. Here we only compute the + number of bytes. Do a double word increment. */ + ctx->total[0] += len; + if (ctx->total[0] < len) + ++ctx->total[1]; + + /* Process all bytes in the buffer with 64 bytes in each round of + the loop. */ + while (words < endp) + { + uint32_t *cwp = correct_words; + uint32_t A_save = A; + uint32_t B_save = B; + uint32_t C_save = C; + uint32_t D_save = D; + + /* First round: using the given function, the context and a constant + the next context is computed. Because the algorithms processing + unit is a 32-bit word and it is determined to work on words in + little endian byte order we perhaps have to change the byte order + before the computation. To reduce the work for the next steps + we store the swapped words in the array CORRECT_WORDS. */ + +#define OP(a, b, c, d, s, T) \ + do \ + { \ + a += FF (b, c, d) + (*cwp++ = SWAP (*words)) + T; \ + ++words; \ + CYCLIC (a, s); \ + a += b; \ + } \ + while (0) + + /* It is unfortunate that C does not provide an operator for + cyclic rotation. Hope the C compiler is smart enough. */ +#define CYCLIC(w, s) (w = (w << s) | (w >> (32 - s))) + + /* Before we start, one word to the strange constants. + They are defined in RFC 1321 as + + T[i] = (int) (4294967296.0 * fabs (sin (i))), i=1..64 + + Here is an equivalent invocation using Perl: + + perl -e 'foreach(1..64){printf "0x%08x\n", int (4294967296 * abs (sin $_))}' + */ + + /* Round 1. */ + OP (A, B, C, D, 7, 0xd76aa478); + OP (D, A, B, C, 12, 0xe8c7b756); + OP (C, D, A, B, 17, 0x242070db); + OP (B, C, D, A, 22, 0xc1bdceee); + OP (A, B, C, D, 7, 0xf57c0faf); + OP (D, A, B, C, 12, 0x4787c62a); + OP (C, D, A, B, 17, 0xa8304613); + OP (B, C, D, A, 22, 0xfd469501); + OP (A, B, C, D, 7, 0x698098d8); + OP (D, A, B, C, 12, 0x8b44f7af); + OP (C, D, A, B, 17, 0xffff5bb1); + OP (B, C, D, A, 22, 0x895cd7be); + OP (A, B, C, D, 7, 0x6b901122); + OP (D, A, B, C, 12, 0xfd987193); + OP (C, D, A, B, 17, 0xa679438e); + OP (B, C, D, A, 22, 0x49b40821); + + /* For the second to fourth round we have the possibly swapped words + in CORRECT_WORDS. Redefine the macro to take an additional first + argument specifying the function to use. */ +#undef OP +#define OP(f, a, b, c, d, k, s, T) \ + do \ + { \ + a += f (b, c, d) + correct_words[k] + T; \ + CYCLIC (a, s); \ + a += b; \ + } \ + while (0) + + /* Round 2. */ + OP (FG, A, B, C, D, 1, 5, 0xf61e2562); + OP (FG, D, A, B, C, 6, 9, 0xc040b340); + OP (FG, C, D, A, B, 11, 14, 0x265e5a51); + OP (FG, B, C, D, A, 0, 20, 0xe9b6c7aa); + OP (FG, A, B, C, D, 5, 5, 0xd62f105d); + OP (FG, D, A, B, C, 10, 9, 0x02441453); + OP (FG, C, D, A, B, 15, 14, 0xd8a1e681); + OP (FG, B, C, D, A, 4, 20, 0xe7d3fbc8); + OP (FG, A, B, C, D, 9, 5, 0x21e1cde6); + OP (FG, D, A, B, C, 14, 9, 0xc33707d6); + OP (FG, C, D, A, B, 3, 14, 0xf4d50d87); + OP (FG, B, C, D, A, 8, 20, 0x455a14ed); + OP (FG, A, B, C, D, 13, 5, 0xa9e3e905); + OP (FG, D, A, B, C, 2, 9, 0xfcefa3f8); + OP (FG, C, D, A, B, 7, 14, 0x676f02d9); + OP (FG, B, C, D, A, 12, 20, 0x8d2a4c8a); + + /* Round 3. */ + OP (FH, A, B, C, D, 5, 4, 0xfffa3942); + OP (FH, D, A, B, C, 8, 11, 0x8771f681); + OP (FH, C, D, A, B, 11, 16, 0x6d9d6122); + OP (FH, B, C, D, A, 14, 23, 0xfde5380c); + OP (FH, A, B, C, D, 1, 4, 0xa4beea44); + OP (FH, D, A, B, C, 4, 11, 0x4bdecfa9); + OP (FH, C, D, A, B, 7, 16, 0xf6bb4b60); + OP (FH, B, C, D, A, 10, 23, 0xbebfbc70); + OP (FH, A, B, C, D, 13, 4, 0x289b7ec6); + OP (FH, D, A, B, C, 0, 11, 0xeaa127fa); + OP (FH, C, D, A, B, 3, 16, 0xd4ef3085); + OP (FH, B, C, D, A, 6, 23, 0x04881d05); + OP (FH, A, B, C, D, 9, 4, 0xd9d4d039); + OP (FH, D, A, B, C, 12, 11, 0xe6db99e5); + OP (FH, C, D, A, B, 15, 16, 0x1fa27cf8); + OP (FH, B, C, D, A, 2, 23, 0xc4ac5665); + + /* Round 4. */ + OP (FI, A, B, C, D, 0, 6, 0xf4292244); + OP (FI, D, A, B, C, 7, 10, 0x432aff97); + OP (FI, C, D, A, B, 14, 15, 0xab9423a7); + OP (FI, B, C, D, A, 5, 21, 0xfc93a039); + OP (FI, A, B, C, D, 12, 6, 0x655b59c3); + OP (FI, D, A, B, C, 3, 10, 0x8f0ccc92); + OP (FI, C, D, A, B, 10, 15, 0xffeff47d); + OP (FI, B, C, D, A, 1, 21, 0x85845dd1); + OP (FI, A, B, C, D, 8, 6, 0x6fa87e4f); + OP (FI, D, A, B, C, 15, 10, 0xfe2ce6e0); + OP (FI, C, D, A, B, 6, 15, 0xa3014314); + OP (FI, B, C, D, A, 13, 21, 0x4e0811a1); + OP (FI, A, B, C, D, 4, 6, 0xf7537e82); + OP (FI, D, A, B, C, 11, 10, 0xbd3af235); + OP (FI, C, D, A, B, 2, 15, 0x2ad7d2bb); + OP (FI, B, C, D, A, 9, 21, 0xeb86d391); + + /* Add the starting values of the context. */ + A += A_save; + B += B_save; + C += C_save; + D += D_save; + } + + /* Put checksum in context given as argument. */ + ctx->A = A; + ctx->B = B; + ctx->C = C; + ctx->D = D; +} diff --git a/src/daemon/https/lgl/md5.h b/src/daemon/https/lgl/md5.h new file mode 100644 index 00000000..6018a6f6 --- /dev/null +++ b/src/daemon/https/lgl/md5.h @@ -0,0 +1,124 @@ +/* Declaration of functions and data types used for MD5 sum computing + library functions. + Copyright (C) 1995-1997,1999,2000,2001,2004,2005,2006 + Free Software Foundation, Inc. + This file is part of the GNU C Library. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by the + Free Software Foundation; either version 2.1, or (at your option) any + later version. + + This program 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef _MD5_H +#define _MD5_H 1 + +#include +#include + +#define MD5_DIGEST_SIZE 16 +#define MD5_BLOCK_SIZE 64 + +#ifndef __GNUC_PREREQ +# if defined __GNUC__ && defined __GNUC_MINOR__ +# define __GNUC_PREREQ(maj, min) \ + ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min)) +# else +# define __GNUC_PREREQ(maj, min) 0 +# endif +#endif + +#ifndef __THROW +# if defined __cplusplus && __GNUC_PREREQ (2,8) +# define __THROW throw () +# else +# define __THROW +# endif +#endif + +#ifndef _LIBC +# define __md5_buffer md5_buffer +# define __md5_finish_ctx md5_finish_ctx +# define __md5_init_ctx md5_init_ctx +# define __md5_process_block md5_process_block +# define __md5_process_bytes md5_process_bytes +# define __md5_read_ctx md5_read_ctx +# define __md5_stream md5_stream +#endif + +/* Structure to save state of computation between the single steps. */ +struct md5_ctx +{ + uint32_t A; + uint32_t B; + uint32_t C; + uint32_t D; + + uint32_t total[2]; + uint32_t buflen; + uint32_t buffer[32]; +}; + +/* + * The following three functions are build up the low level used in + * the functions `md5_stream' and `md5_buffer'. + */ + +/* Initialize structure containing state of computation. + (RFC 1321, 3.3: Step 3) */ +extern void __md5_init_ctx (struct md5_ctx *ctx) __THROW; + +/* Starting with the result of former calls of this function (or the + initialization function update the context for the next LEN bytes + starting at BUFFER. + It is necessary that LEN is a multiple of 64!!! */ +extern void __md5_process_block (const void *buffer, size_t len, + struct md5_ctx *ctx) __THROW; + +/* Starting with the result of former calls of this function (or the + initialization function update the context for the next LEN bytes + starting at BUFFER. + It is NOT required that LEN is a multiple of 64. */ +extern void __md5_process_bytes (const void *buffer, size_t len, + struct md5_ctx *ctx) __THROW; + +/* Process the remaining bytes in the buffer and put result from CTX + in first 16 bytes following RESBUF. The result is always in little + endian byte order, so that a byte-wise output yields to the wanted + ASCII representation of the message digest. + + IMPORTANT: On some systems, RESBUF must be aligned to a 32-bit + boundary. */ +extern void *__md5_finish_ctx (struct md5_ctx *ctx, void *resbuf) __THROW; + + +/* Put result from CTX in first 16 bytes following RESBUF. The result is + always in little endian byte order, so that a byte-wise output yields + to the wanted ASCII representation of the message digest. + + IMPORTANT: On some systems, RESBUF must be aligned to a 32-bit + boundary. */ +extern void *__md5_read_ctx (const struct md5_ctx *ctx, void *resbuf) __THROW; + + +/* Compute MD5 message digest for bytes read from STREAM. The + resulting message digest number will be written into the 16 bytes + beginning at RESBLOCK. */ +extern int __md5_stream (FILE *stream, void *resblock) __THROW; + +/* Compute MD5 message digest for LEN bytes beginning at BUFFER. The + result is always in little endian byte order, so that a byte-wise + output yields to the wanted ASCII representation of the message + digest. */ +extern void *__md5_buffer (const char *buffer, size_t len, + void *resblock) __THROW; + +#endif /* md5.h */ diff --git a/src/daemon/https/lgl/memmem.c b/src/daemon/https/lgl/memmem.c new file mode 100644 index 00000000..1b66ed56 --- /dev/null +++ b/src/daemon/https/lgl/memmem.c @@ -0,0 +1,61 @@ +/* Copyright (C) 1991,92,93,94,96,97,98,2000,2004,2007 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1, or (at your option) + any later version. + + This program 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef _LIBC +# include +#endif + +#include +#include + +#ifndef _LIBC +# define __builtin_expect(expr, val) (expr) +#endif + +#undef memmem + +/* Return the first occurrence of NEEDLE in HAYSTACK. */ +void * +memmem (haystack, haystack_len, needle, needle_len) + const void *haystack; + size_t haystack_len; + const void *needle; + size_t needle_len; +{ + const char *begin; + const char *const last_possible = (const char *) haystack + haystack_len + - needle_len; + + if (needle_len == 0) + /* The first occurrence of the empty string is deemed to occur at + the beginning of the string. */ + return (void *) haystack; + + /* Sanity check, otherwise the loop might search through the whole + memory. */ + if (__builtin_expect (haystack_len < needle_len, 0)) + return NULL; + + for (begin = (const char *) haystack; begin <= last_possible; ++begin) + if (begin[0] == ((const char *) needle)[0] + && !memcmp ((const void *) &begin[1], + (const void *) ((const char *) needle + 1), + needle_len - 1)) + return (void *) begin; + + return NULL; +} diff --git a/src/daemon/https/lgl/memmove.c b/src/daemon/https/lgl/memmove.c new file mode 100644 index 00000000..0f040540 --- /dev/null +++ b/src/daemon/https/lgl/memmove.c @@ -0,0 +1,26 @@ +/* memmove.c -- copy memory. + Copy LENGTH bytes from SOURCE to DEST. Does not null-terminate. + In the public domain. + By David MacKenzie . */ + +#include + +#include + +void * +memmove (void *dest0, void const *source0, size_t length) +{ + char *dest = dest0; + char const *source = source0; + if (source < dest) + /* Moving from low mem to hi mem; start at end. */ + for (source += length, dest += length; length; --length) + *--dest = *--source; + else if (source != dest) + { + /* Moving from hi mem to low mem; start at beginning. */ + for (; length; --length) + *dest++ = *source++; + } + return dest0; +} diff --git a/src/daemon/https/lgl/memxor.c b/src/daemon/https/lgl/memxor.c new file mode 100644 index 00000000..7b0b6ae9 --- /dev/null +++ b/src/daemon/https/lgl/memxor.c @@ -0,0 +1,35 @@ +/* memxor.c -- perform binary exclusive OR operation of two memory blocks. + Copyright (C) 2005, 2006 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1, or (at your option) + any later version. + + This program 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Written by Simon Josefsson. The interface was inspired by memxor + in Niels Möller's Nettle. */ + +#include + +#include "memxor.h" + +void * +memxor (void *restrict dest, const void *restrict src, size_t n) +{ + char const *s = src; + char *d = dest; + + for (; n > 0; n--) + *d++ ^= *s++; + + return dest; +} diff --git a/src/daemon/https/lgl/memxor.h b/src/daemon/https/lgl/memxor.h new file mode 100644 index 00000000..4f85f2d7 --- /dev/null +++ b/src/daemon/https/lgl/memxor.h @@ -0,0 +1,31 @@ +/* memxor.h -- perform binary exclusive OR operation on memory blocks. + Copyright (C) 2005 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1, or (at your option) + any later version. + + This program 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Written by Simon Josefsson. The interface was inspired by memxor + in Niels Möller's Nettle. */ + +#ifndef MEMXOR_H +# define MEMXOR_H + +#include + +/* Compute binary exclusive OR of memory areas DEST and SRC, putting + the result in DEST, of length N bytes. Returns a pointer to + DEST. */ +void *memxor (void *restrict dest, const void *restrict src, size_t n); + +#endif /* MEMXOR_H */ diff --git a/src/daemon/https/lgl/printf-args.c b/src/daemon/https/lgl/printf-args.c new file mode 100644 index 00000000..55a5c439 --- /dev/null +++ b/src/daemon/https/lgl/printf-args.c @@ -0,0 +1,185 @@ +/* Decomposed printf argument list. + Copyright (C) 1999, 2002-2003, 2005-2007 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1, or (at your option) + any later version. + + This program 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* This file can be parametrized with the following macros: + ENABLE_UNISTDIO Set to 1 to enable the unistdio extensions. + PRINTF_FETCHARGS Name of the function to be defined. + STATIC Set to 'static' to declare the function static. */ + +#ifndef PRINTF_FETCHARGS +# include +#endif + +/* Specification. */ +#ifndef PRINTF_FETCHARGS +# include "printf-args.h" +#endif + +#ifdef STATIC +STATIC +#endif + int +PRINTF_FETCHARGS (va_list args, arguments * a) +{ + size_t i; + argument *ap; + + for (i = 0, ap = &a->arg[0]; i < a->count; i++, ap++) + switch (ap->type) + { + case TYPE_SCHAR: + ap->a.a_schar = va_arg (args, /*signed char */ int); + break; + case TYPE_UCHAR: + ap->a.a_uchar = va_arg (args, /*unsigned char */ int); + break; + case TYPE_SHORT: + ap->a.a_short = va_arg (args, /*short */ int); + break; + case TYPE_USHORT: + ap->a.a_ushort = va_arg (args, /*unsigned short */ int); + break; + case TYPE_INT: + ap->a.a_int = va_arg (args, int); + break; + case TYPE_UINT: + ap->a.a_uint = va_arg (args, unsigned int); + break; + case TYPE_LONGINT: + ap->a.a_longint = va_arg (args, long int); + break; + case TYPE_ULONGINT: + ap->a.a_ulongint = va_arg (args, unsigned long int); + break; +#if HAVE_LONG_LONG_INT + case TYPE_LONGLONGINT: + ap->a.a_longlongint = va_arg (args, long long int); + break; + case TYPE_ULONGLONGINT: + ap->a.a_ulonglongint = va_arg (args, unsigned long long int); + break; +#endif + case TYPE_DOUBLE: + ap->a.a_double = va_arg (args, double); + break; + case TYPE_LONGDOUBLE: + ap->a.a_longdouble = va_arg (args, long double); + break; + case TYPE_CHAR: + ap->a.a_char = va_arg (args, int); + break; +#if HAVE_WINT_T + case TYPE_WIDE_CHAR: + /* Although ISO C 99 7.24.1.(2) says that wint_t is "unchanged by + default argument promotions", this is not the case in mingw32, + where wint_t is 'unsigned short'. */ + ap->a.a_wide_char = + (sizeof (wint_t) < sizeof (int) + ? va_arg (args, int) : va_arg (args, wint_t)); + break; +#endif + case TYPE_STRING: + ap->a.a_string = va_arg (args, const char *); + /* A null pointer is an invalid argument for "%s", but in practice + it occurs quite frequently in printf statements that produce + debug output. Use a fallback in this case. */ + if (ap->a.a_string == NULL) + ap->a.a_string = "(NULL)"; + break; +#if HAVE_WCHAR_T + case TYPE_WIDE_STRING: + ap->a.a_wide_string = va_arg (args, const wchar_t *); + /* A null pointer is an invalid argument for "%ls", but in practice + it occurs quite frequently in printf statements that produce + debug output. Use a fallback in this case. */ + if (ap->a.a_wide_string == NULL) + { + static const wchar_t wide_null_string[] = { + (wchar_t) '(', + (wchar_t) 'N', (wchar_t) 'U', (wchar_t) 'L', (wchar_t) 'L', + (wchar_t) ')', + (wchar_t) 0 + }; + ap->a.a_wide_string = wide_null_string; + } + break; +#endif + case TYPE_POINTER: + ap->a.a_pointer = va_arg (args, void *); + break; + case TYPE_COUNT_SCHAR_POINTER: + ap->a.a_count_schar_pointer = va_arg (args, signed char *); + break; + case TYPE_COUNT_SHORT_POINTER: + ap->a.a_count_short_pointer = va_arg (args, short *); + break; + case TYPE_COUNT_INT_POINTER: + ap->a.a_count_int_pointer = va_arg (args, int *); + break; + case TYPE_COUNT_LONGINT_POINTER: + ap->a.a_count_longint_pointer = va_arg (args, long int *); + break; +#if HAVE_LONG_LONG_INT + case TYPE_COUNT_LONGLONGINT_POINTER: + ap->a.a_count_longlongint_pointer = va_arg (args, long long int *); + break; +#endif +#if ENABLE_UNISTDIO + /* The unistdio extensions. */ + case TYPE_U8_STRING: + ap->a.a_u8_string = va_arg (args, const uint8_t *); + /* A null pointer is an invalid argument for "%U", but in practice + it occurs quite frequently in printf statements that produce + debug output. Use a fallback in this case. */ + if (ap->a.a_u8_string == NULL) + { + static const uint8_t u8_null_string[] = + { '(', 'N', 'U', 'L', 'L', ')', 0 }; + ap->a.a_u8_string = u8_null_string; + } + break; + case TYPE_U16_STRING: + ap->a.a_u16_string = va_arg (args, const uint16_t *); + /* A null pointer is an invalid argument for "%lU", but in practice + it occurs quite frequently in printf statements that produce + debug output. Use a fallback in this case. */ + if (ap->a.a_u16_string == NULL) + { + static const uint16_t u16_null_string[] = + { '(', 'N', 'U', 'L', 'L', ')', 0 }; + ap->a.a_u16_string = u16_null_string; + } + break; + case TYPE_U32_STRING: + ap->a.a_u32_string = va_arg (args, const uint32_t *); + /* A null pointer is an invalid argument for "%llU", but in practice + it occurs quite frequently in printf statements that produce + debug output. Use a fallback in this case. */ + if (ap->a.a_u32_string == NULL) + { + static const uint32_t u32_null_string[] = + { '(', 'N', 'U', 'L', 'L', ')', 0 }; + ap->a.a_u32_string = u32_null_string; + } + break; +#endif + default: + /* Unknown type. */ + return -1; + } + return 0; +} diff --git a/src/daemon/https/lgl/printf-args.h b/src/daemon/https/lgl/printf-args.h new file mode 100644 index 00000000..b663a63b --- /dev/null +++ b/src/daemon/https/lgl/printf-args.h @@ -0,0 +1,154 @@ +/* Decomposed printf argument list. + Copyright (C) 1999, 2002-2003, 2006-2007 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1, or (at your option) + any later version. + + This program 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef _PRINTF_ARGS_H +#define _PRINTF_ARGS_H + +/* This file can be parametrized with the following macros: + ENABLE_UNISTDIO Set to 1 to enable the unistdio extensions. + PRINTF_FETCHARGS Name of the function to be declared. + STATIC Set to 'static' to declare the function static. */ + +/* Default parameters. */ +#ifndef PRINTF_FETCHARGS +# define PRINTF_FETCHARGS printf_fetchargs +#endif + +/* Get size_t. */ +#include + +/* Get wchar_t. */ +#if HAVE_WCHAR_T +# include +#endif + +/* Get wint_t. */ +#if HAVE_WINT_T +# include +#endif + +/* Get va_list. */ +#include + + +/* Argument types */ +typedef enum +{ + TYPE_NONE, + TYPE_SCHAR, + TYPE_UCHAR, + TYPE_SHORT, + TYPE_USHORT, + TYPE_INT, + TYPE_UINT, + TYPE_LONGINT, + TYPE_ULONGINT, +#if HAVE_LONG_LONG_INT + TYPE_LONGLONGINT, + TYPE_ULONGLONGINT, +#endif + TYPE_DOUBLE, + TYPE_LONGDOUBLE, + TYPE_CHAR, +#if HAVE_WINT_T + TYPE_WIDE_CHAR, +#endif + TYPE_STRING, +#if HAVE_WCHAR_T + TYPE_WIDE_STRING, +#endif + TYPE_POINTER, + TYPE_COUNT_SCHAR_POINTER, + TYPE_COUNT_SHORT_POINTER, + TYPE_COUNT_INT_POINTER, + TYPE_COUNT_LONGINT_POINTER +#if HAVE_LONG_LONG_INT +, TYPE_COUNT_LONGLONGINT_POINTER +#endif +#if ENABLE_UNISTDIO + /* The unistdio extensions. */ +, TYPE_U8_STRING +, TYPE_U16_STRING +, TYPE_U32_STRING +#endif +} arg_type; + +/* Polymorphic argument */ +typedef struct +{ + arg_type type; + union + { + signed char a_schar; + unsigned char a_uchar; + short a_short; + unsigned short a_ushort; + int a_int; + unsigned int a_uint; + long int a_longint; + unsigned long int a_ulongint; +#if HAVE_LONG_LONG_INT + long long int a_longlongint; + unsigned long long int a_ulonglongint; +#endif + float a_float; + double a_double; + long double a_longdouble; + int a_char; +#if HAVE_WINT_T + wint_t a_wide_char; +#endif + const char* a_string; +#if HAVE_WCHAR_T + const wchar_t* a_wide_string; +#endif + void* a_pointer; + signed char * a_count_schar_pointer; + short * a_count_short_pointer; + int * a_count_int_pointer; + long int * a_count_longint_pointer; +#if HAVE_LONG_LONG_INT + long long int * a_count_longlongint_pointer; +#endif +#if ENABLE_UNISTDIO + /* The unistdio extensions. */ + const uint8_t * a_u8_string; + const uint16_t * a_u16_string; + const uint32_t * a_u32_string; +#endif + } + a; +} +argument; + +typedef struct +{ + size_t count; + argument *arg; +} +arguments; + + +/* Fetch the arguments, putting them into a. */ +#ifdef STATIC +STATIC +#else +extern +#endif +int PRINTF_FETCHARGS (va_list args, arguments *a); + +#endif /* _PRINTF_ARGS_H */ diff --git a/src/daemon/https/lgl/printf-parse.c b/src/daemon/https/lgl/printf-parse.c new file mode 100644 index 00000000..e0b83a39 --- /dev/null +++ b/src/daemon/https/lgl/printf-parse.c @@ -0,0 +1,599 @@ +/* Formatted output to strings. + Copyright (C) 1999-2000, 2002-2003, 2006-2007 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1, or (at your option) + any later version. + + This program 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* This file can be parametrized with the following macros: + CHAR_T The element type of the format string. + CHAR_T_ONLY_ASCII Set to 1 to enable verification that all characters + in the format string are ASCII. + DIRECTIVE Structure denoting a format directive. + Depends on CHAR_T. + DIRECTIVES Structure denoting the set of format directives of a + format string. Depends on CHAR_T. + PRINTF_PARSE Function that parses a format string. + Depends on CHAR_T. + STATIC Set to 'static' to declare the function static. + ENABLE_UNISTDIO Set to 1 to enable the unistdio extensions. */ + +#ifndef PRINTF_PARSE +# include +#endif + +/* Specification. */ +#ifndef PRINTF_PARSE +# include "printf-parse.h" +#endif + +/* Default parameters. */ +#ifndef PRINTF_PARSE +# define PRINTF_PARSE printf_parse +# define CHAR_T char +# define DIRECTIVE char_directive +# define DIRECTIVES char_directives +#endif + +/* Get size_t, NULL. */ +#include + +/* Get intmax_t. */ +#if defined IN_LIBINTL || defined IN_LIBASPRINTF +# if HAVE_STDINT_H_WITH_UINTMAX +# include +# endif +# if HAVE_INTTYPES_H_WITH_UINTMAX +# include +# endif +#else +# include +#endif + +/* malloc(), realloc(), free(). */ +#include + +/* errno. */ +#include + +/* Checked size_t computations. */ +#include "xsize.h" + +#if CHAR_T_ONLY_ASCII +/* c_isascii(). */ +# include "c-ctype.h" +#endif + +#ifdef STATIC +STATIC +#endif + int +PRINTF_PARSE (const CHAR_T * format, DIRECTIVES * d, arguments * a) +{ + const CHAR_T *cp = format; /* pointer into format */ + size_t arg_posn = 0; /* number of regular arguments consumed */ + size_t d_allocated; /* allocated elements of d->dir */ + size_t a_allocated; /* allocated elements of a->arg */ + size_t max_width_length = 0; + size_t max_precision_length = 0; + + d->count = 0; + d_allocated = 1; + d->dir = (DIRECTIVE *) malloc (d_allocated * sizeof (DIRECTIVE)); + if (d->dir == NULL) + /* Out of memory. */ + goto out_of_memory_1; + + a->count = 0; + a_allocated = 0; + a->arg = NULL; + +#define REGISTER_ARG(_index_,_type_) \ + { \ + size_t n = (_index_); \ + if (n >= a_allocated) \ + { \ + size_t memory_size; \ + argument *memory; \ + \ + a_allocated = xtimes (a_allocated, 2); \ + if (a_allocated <= n) \ + a_allocated = xsum (n, 1); \ + memory_size = xtimes (a_allocated, sizeof (argument)); \ + if (size_overflow_p (memory_size)) \ + /* Overflow, would lead to out of memory. */ \ + goto out_of_memory; \ + memory = (argument *) (a->arg \ + ? realloc (a->arg, memory_size) \ + : malloc (memory_size)); \ + if (memory == NULL) \ + /* Out of memory. */ \ + goto out_of_memory; \ + a->arg = memory; \ + } \ + while (a->count <= n) \ + a->arg[a->count++].type = TYPE_NONE; \ + if (a->arg[n].type == TYPE_NONE) \ + a->arg[n].type = (_type_); \ + else if (a->arg[n].type != (_type_)) \ + /* Ambiguous type for positional argument. */ \ + goto error; \ + } + + while (*cp != '\0') + { + CHAR_T c = *cp++; + if (c == '%') + { + size_t arg_index = ARG_NONE; + DIRECTIVE *dp = &d->dir[d->count]; /* pointer to next directive */ + + /* Initialize the next directive. */ + dp->dir_start = cp - 1; + dp->flags = 0; + dp->width_start = NULL; + dp->width_end = NULL; + dp->width_arg_index = ARG_NONE; + dp->precision_start = NULL; + dp->precision_end = NULL; + dp->precision_arg_index = ARG_NONE; + dp->arg_index = ARG_NONE; + + /* Test for positional argument. */ + if (*cp >= '0' && *cp <= '9') + { + const CHAR_T *np; + + for (np = cp; *np >= '0' && *np <= '9'; np++) + ; + if (*np == '$') + { + size_t n = 0; + + for (np = cp; *np >= '0' && *np <= '9'; np++) + n = xsum (xtimes (n, 10), *np - '0'); + if (n == 0) + /* Positional argument 0. */ + goto error; + if (size_overflow_p (n)) + /* n too large, would lead to out of memory later. */ + goto error; + arg_index = n - 1; + cp = np + 1; + } + } + + /* Read the flags. */ + for (;;) + { + if (*cp == '\'') + { + dp->flags |= FLAG_GROUP; + cp++; + } + else if (*cp == '-') + { + dp->flags |= FLAG_LEFT; + cp++; + } + else if (*cp == '+') + { + dp->flags |= FLAG_SHOWSIGN; + cp++; + } + else if (*cp == ' ') + { + dp->flags |= FLAG_SPACE; + cp++; + } + else if (*cp == '#') + { + dp->flags |= FLAG_ALT; + cp++; + } + else if (*cp == '0') + { + dp->flags |= FLAG_ZERO; + cp++; + } + else + break; + } + + /* Parse the field width. */ + if (*cp == '*') + { + dp->width_start = cp; + cp++; + dp->width_end = cp; + if (max_width_length < 1) + max_width_length = 1; + + /* Test for positional argument. */ + if (*cp >= '0' && *cp <= '9') + { + const CHAR_T *np; + + for (np = cp; *np >= '0' && *np <= '9'; np++) + ; + if (*np == '$') + { + size_t n = 0; + + for (np = cp; *np >= '0' && *np <= '9'; np++) + n = xsum (xtimes (n, 10), *np - '0'); + if (n == 0) + /* Positional argument 0. */ + goto error; + if (size_overflow_p (n)) + /* n too large, would lead to out of memory later. */ + goto error; + dp->width_arg_index = n - 1; + cp = np + 1; + } + } + if (dp->width_arg_index == ARG_NONE) + { + dp->width_arg_index = arg_posn++; + if (dp->width_arg_index == ARG_NONE) + /* arg_posn wrapped around. */ + goto error; + } + REGISTER_ARG (dp->width_arg_index, TYPE_INT); + } + else if (*cp >= '0' && *cp <= '9') + { + size_t width_length; + + dp->width_start = cp; + for (; *cp >= '0' && *cp <= '9'; cp++) + ; + dp->width_end = cp; + width_length = dp->width_end - dp->width_start; + if (max_width_length < width_length) + max_width_length = width_length; + } + + /* Parse the precision. */ + if (*cp == '.') + { + cp++; + if (*cp == '*') + { + dp->precision_start = cp - 1; + cp++; + dp->precision_end = cp; + if (max_precision_length < 2) + max_precision_length = 2; + + /* Test for positional argument. */ + if (*cp >= '0' && *cp <= '9') + { + const CHAR_T *np; + + for (np = cp; *np >= '0' && *np <= '9'; np++) + ; + if (*np == '$') + { + size_t n = 0; + + for (np = cp; *np >= '0' && *np <= '9'; np++) + n = xsum (xtimes (n, 10), *np - '0'); + if (n == 0) + /* Positional argument 0. */ + goto error; + if (size_overflow_p (n)) + /* n too large, would lead to out of memory + later. */ + goto error; + dp->precision_arg_index = n - 1; + cp = np + 1; + } + } + if (dp->precision_arg_index == ARG_NONE) + { + dp->precision_arg_index = arg_posn++; + if (dp->precision_arg_index == ARG_NONE) + /* arg_posn wrapped around. */ + goto error; + } + REGISTER_ARG (dp->precision_arg_index, TYPE_INT); + } + else + { + size_t precision_length; + + dp->precision_start = cp - 1; + for (; *cp >= '0' && *cp <= '9'; cp++) + ; + dp->precision_end = cp; + precision_length = dp->precision_end - dp->precision_start; + if (max_precision_length < precision_length) + max_precision_length = precision_length; + } + } + + { + arg_type type; + + /* Parse argument type/size specifiers. */ + { + int flags = 0; + + for (;;) + { + if (*cp == 'h') + { + flags |= (1 << (flags & 1)); + cp++; + } + else if (*cp == 'L') + { + flags |= 4; + cp++; + } + else if (*cp == 'l') + { + flags += 8; + cp++; + } + else if (*cp == 'j') + { + if (sizeof (intmax_t) > sizeof (long)) + { + /* intmax_t = long long */ + flags += 16; + } + else if (sizeof (intmax_t) > sizeof (int)) + { + /* intmax_t = long */ + flags += 8; + } + cp++; + } + else if (*cp == 'z' || *cp == 'Z') + { + /* 'z' is standardized in ISO C 99, but glibc uses 'Z' + because the warning facility in gcc-2.95.2 understands + only 'Z' (see gcc-2.95.2/gcc/c-common.c:1784). */ + if (sizeof (size_t) > sizeof (long)) + { + /* size_t = long long */ + flags += 16; + } + else if (sizeof (size_t) > sizeof (int)) + { + /* size_t = long */ + flags += 8; + } + cp++; + } + else if (*cp == 't') + { + if (sizeof (ptrdiff_t) > sizeof (long)) + { + /* ptrdiff_t = long long */ + flags += 16; + } + else if (sizeof (ptrdiff_t) > sizeof (int)) + { + /* ptrdiff_t = long */ + flags += 8; + } + cp++; + } + else + break; + } + + /* Read the conversion character. */ + c = *cp++; + switch (c) + { + case 'd': + case 'i': +#if HAVE_LONG_LONG_INT + /* If 'long long' exists and is larger than 'long': */ + if (flags >= 16 || (flags & 4)) + type = TYPE_LONGLONGINT; + else +#endif + /* If 'long long' exists and is the same as 'long', we parse + "lld" into TYPE_LONGINT. */ + if (flags >= 8) + type = TYPE_LONGINT; + else if (flags & 2) + type = TYPE_SCHAR; + else if (flags & 1) + type = TYPE_SHORT; + else + type = TYPE_INT; + break; + case 'o': + case 'u': + case 'x': + case 'X': +#if HAVE_LONG_LONG_INT + /* If 'long long' exists and is larger than 'long': */ + if (flags >= 16 || (flags & 4)) + type = TYPE_ULONGLONGINT; + else +#endif + /* If 'unsigned long long' exists and is the same as + 'unsigned long', we parse "llu" into TYPE_ULONGINT. */ + if (flags >= 8) + type = TYPE_ULONGINT; + else if (flags & 2) + type = TYPE_UCHAR; + else if (flags & 1) + type = TYPE_USHORT; + else + type = TYPE_UINT; + break; + case 'f': + case 'F': + case 'e': + case 'E': + case 'g': + case 'G': + case 'a': + case 'A': + if (flags >= 16 || (flags & 4)) + type = TYPE_LONGDOUBLE; + else + type = TYPE_DOUBLE; + break; + case 'c': + if (flags >= 8) +#if HAVE_WINT_T + type = TYPE_WIDE_CHAR; +#else + goto error; +#endif + else + type = TYPE_CHAR; + break; +#if HAVE_WINT_T + case 'C': + type = TYPE_WIDE_CHAR; + c = 'c'; + break; +#endif + case 's': + if (flags >= 8) +#if HAVE_WCHAR_T + type = TYPE_WIDE_STRING; +#else + goto error; +#endif + else + type = TYPE_STRING; + break; +#if HAVE_WCHAR_T + case 'S': + type = TYPE_WIDE_STRING; + c = 's'; + break; +#endif + case 'p': + type = TYPE_POINTER; + break; + case 'n': +#if HAVE_LONG_LONG_INT + /* If 'long long' exists and is larger than 'long': */ + if (flags >= 16 || (flags & 4)) + type = TYPE_COUNT_LONGLONGINT_POINTER; + else +#endif + /* If 'long long' exists and is the same as 'long', we parse + "lln" into TYPE_COUNT_LONGINT_POINTER. */ + if (flags >= 8) + type = TYPE_COUNT_LONGINT_POINTER; + else if (flags & 2) + type = TYPE_COUNT_SCHAR_POINTER; + else if (flags & 1) + type = TYPE_COUNT_SHORT_POINTER; + else + type = TYPE_COUNT_INT_POINTER; + break; +#if ENABLE_UNISTDIO + /* The unistdio extensions. */ + case 'U': + if (flags >= 16) + type = TYPE_U32_STRING; + else if (flags >= 8) + type = TYPE_U16_STRING; + else + type = TYPE_U8_STRING; + break; +#endif + case '%': + type = TYPE_NONE; + break; + default: + /* Unknown conversion character. */ + goto error; + } + } + + if (type != TYPE_NONE) + { + dp->arg_index = arg_index; + if (dp->arg_index == ARG_NONE) + { + dp->arg_index = arg_posn++; + if (dp->arg_index == ARG_NONE) + /* arg_posn wrapped around. */ + goto error; + } + REGISTER_ARG (dp->arg_index, type); + } + dp->conversion = c; + dp->dir_end = cp; + } + + d->count++; + if (d->count >= d_allocated) + { + size_t memory_size; + DIRECTIVE *memory; + + d_allocated = xtimes (d_allocated, 2); + memory_size = xtimes (d_allocated, sizeof (DIRECTIVE)); + if (size_overflow_p (memory_size)) + /* Overflow, would lead to out of memory. */ + goto out_of_memory; + memory = (DIRECTIVE *) realloc (d->dir, memory_size); + if (memory == NULL) + /* Out of memory. */ + goto out_of_memory; + d->dir = memory; + } + } +#if CHAR_T_ONLY_ASCII + else if (!c_isascii (c)) + { + /* Non-ASCII character. Not supported. */ + goto error; + } +#endif + } + d->dir[d->count].dir_start = cp; + + d->max_width_length = max_width_length; + d->max_precision_length = max_precision_length; + return 0; + +error: + if (a->arg) + free (a->arg); + if (d->dir) + free (d->dir); + errno = EINVAL; + return -1; + +out_of_memory: + if (a->arg) + free (a->arg); + if (d->dir) + free (d->dir); +out_of_memory_1: + errno = ENOMEM; + return -1; +} + +#undef PRINTF_PARSE +#undef DIRECTIVES +#undef DIRECTIVE +#undef CHAR_T_ONLY_ASCII +#undef CHAR_T diff --git a/src/daemon/https/lgl/printf-parse.h b/src/daemon/https/lgl/printf-parse.h new file mode 100644 index 00000000..f9013278 --- /dev/null +++ b/src/daemon/https/lgl/printf-parse.h @@ -0,0 +1,178 @@ +/* Parse printf format string. + Copyright (C) 1999, 2002-2003, 2005, 2007 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1, or (at your option) + any later version. + + This program 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef _PRINTF_PARSE_H +#define _PRINTF_PARSE_H + +/* This file can be parametrized with the following macros: + ENABLE_UNISTDIO Set to 1 to enable the unistdio extensions. + STATIC Set to 'static' to declare the function static. */ + +#include "printf-args.h" + +/* Flags */ +#define FLAG_GROUP 1 /* ' flag */ +#define FLAG_LEFT 2 /* - flag */ +#define FLAG_SHOWSIGN 4 /* + flag */ +#define FLAG_SPACE 8 /* space flag */ +#define FLAG_ALT 16 /* # flag */ +#define FLAG_ZERO 32 + +/* arg_index value indicating that no argument is consumed. */ +#define ARG_NONE (~(size_t)0) + +/* xxx_directive: A parsed directive. + xxx_directives: A parsed format string. */ + +/* A parsed directive. */ +typedef struct +{ + const char* dir_start; + const char* dir_end; + int flags; + const char* width_start; + const char* width_end; + size_t width_arg_index; + const char* precision_start; + const char* precision_end; + size_t precision_arg_index; + char conversion; /* d i o u x X f F e E g G a A c s p n U % but not C S */ + size_t arg_index; +} +char_directive; + +/* A parsed format string. */ +typedef struct +{ + size_t count; + char_directive *dir; + size_t max_width_length; + size_t max_precision_length; +} +char_directives; + +#if ENABLE_UNISTDIO + +/* A parsed directive. */ +typedef struct +{ + const uint8_t* dir_start; + const uint8_t* dir_end; + int flags; + const uint8_t* width_start; + const uint8_t* width_end; + size_t width_arg_index; + const uint8_t* precision_start; + const uint8_t* precision_end; + size_t precision_arg_index; + uint8_t conversion; /* d i o u x X f F e E g G a A c s p n U % but not C S */ + size_t arg_index; +} +u8_directive; + +/* A parsed format string. */ +typedef struct +{ + size_t count; + u8_directive *dir; + size_t max_width_length; + size_t max_precision_length; +} +u8_directives; + +/* A parsed directive. */ +typedef struct +{ + const uint16_t* dir_start; + const uint16_t* dir_end; + int flags; + const uint16_t* width_start; + const uint16_t* width_end; + size_t width_arg_index; + const uint16_t* precision_start; + const uint16_t* precision_end; + size_t precision_arg_index; + uint16_t conversion; /* d i o u x X f F e E g G a A c s p n U % but not C S */ + size_t arg_index; +} +u16_directive; + +/* A parsed format string. */ +typedef struct +{ + size_t count; + u16_directive *dir; + size_t max_width_length; + size_t max_precision_length; +} +u16_directives; + +/* A parsed directive. */ +typedef struct +{ + const uint32_t* dir_start; + const uint32_t* dir_end; + int flags; + const uint32_t* width_start; + const uint32_t* width_end; + size_t width_arg_index; + const uint32_t* precision_start; + const uint32_t* precision_end; + size_t precision_arg_index; + uint32_t conversion; /* d i o u x X f F e E g G a A c s p n U % but not C S */ + size_t arg_index; +} +u32_directive; + +/* A parsed format string. */ +typedef struct +{ + size_t count; + u32_directive *dir; + size_t max_width_length; + size_t max_precision_length; +} +u32_directives; + +#endif + + +/* Parses the format string. Fills in the number N of directives, and fills + in directives[0], ..., directives[N-1], and sets directives[N].dir_start + to the end of the format string. Also fills in the arg_type fields of the + arguments and the needed count of arguments. */ +#if ENABLE_UNISTDIO +extern int + ulc_printf_parse (const char *format, char_directives *d, arguments *a); +extern int + u8_printf_parse (const uint8_t *format, u8_directives *d, arguments *a); +extern int + u16_printf_parse (const uint16_t *format, u16_directives *d, + arguments *a); +extern int + u32_printf_parse (const uint32_t *format, u32_directives *d, + arguments *a); +#else +# ifdef STATIC +STATIC +# else +extern +# endif +int printf_parse (const char *format, char_directives *d, arguments *a); +#endif + +#endif /* _PRINTF_PARSE_H */ diff --git a/src/daemon/https/lgl/read-file.c b/src/daemon/https/lgl/read-file.c new file mode 100644 index 00000000..9172a6ef --- /dev/null +++ b/src/daemon/https/lgl/read-file.c @@ -0,0 +1,136 @@ +/* read-file.c -- read file contents into a string + Copyright (C) 2006 Free Software Foundation, Inc. + Written by Simon Josefsson and Bruno Haible. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1, or (at your option) + any later version. + + This program 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#include + +#include "read-file.h" + +/* Get realloc, free. */ +#include + +/* Get errno. */ +#include + +/* Read a STREAM and return a newly allocated string with the content, + and set *LENGTH to the length of the string. The string is + zero-terminated, but the terminating zero byte is not counted in + *LENGTH. On errors, *LENGTH is undefined, errno preserves the + values set by system functions (if any), and NULL is returned. */ +char * +fread_file (FILE * stream, size_t * length) +{ + char *buf = NULL; + size_t alloc = 0; + size_t size = 0; + int save_errno; + + for (;;) + { + size_t count; + size_t requested; + + if (size + BUFSIZ + 1 > alloc) + { + char *new_buf; + + alloc += alloc / 2; + if (alloc < size + BUFSIZ + 1) + alloc = size + BUFSIZ + 1; + + new_buf = realloc (buf, alloc); + if (!new_buf) + { + save_errno = errno; + break; + } + + buf = new_buf; + } + + requested = alloc - size - 1; + count = fread (buf + size, 1, requested, stream); + size += count; + + if (count != requested) + { + save_errno = errno; + if (ferror (stream)) + break; + buf[size] = '\0'; + *length = size; + return buf; + } + } + + free (buf); + errno = save_errno; + return NULL; +} + +static char * +internal_read_file (const char *filename, size_t * length, const char *mode) +{ + FILE *stream = fopen (filename, mode); + char *out; + int save_errno; + + if (!stream) + return NULL; + + out = fread_file (stream, length); + + save_errno = errno; + + if (fclose (stream) != 0) + { + if (out) + { + save_errno = errno; + free (out); + } + errno = save_errno; + return NULL; + } + + return out; +} + +/* Open and read the contents of FILENAME, and return a newly + allocated string with the content, and set *LENGTH to the length of + the string. The string is zero-terminated, but the terminating + zero byte is not counted in *LENGTH. On errors, *LENGTH is + undefined, errno preserves the values set by system functions (if + any), and NULL is returned. */ +char * +read_file (const char *filename, size_t * length) +{ + return internal_read_file (filename, length, "r"); +} + +/* Open (on non-POSIX systems, in binary mode) and read the contents + of FILENAME, and return a newly allocated string with the content, + and set LENGTH to the length of the string. The string is + zero-terminated, but the terminating zero byte is not counted in + the LENGTH variable. On errors, *LENGTH is undefined, errno + preserves the values set by system functions (if any), and NULL is + returned. */ +char * +read_binary_file (const char *filename, size_t * length) +{ + return internal_read_file (filename, length, "rb"); +} diff --git a/src/daemon/https/lgl/read-file.h b/src/daemon/https/lgl/read-file.h new file mode 100644 index 00000000..798d841f --- /dev/null +++ b/src/daemon/https/lgl/read-file.h @@ -0,0 +1,34 @@ +/* read-file.h -- read file contents into a string + Copyright (C) 2006 Free Software Foundation, Inc. + Written by Simon Josefsson. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1, or (at your option) + any later version. + + This program 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef READ_FILE_H +#define READ_FILE_H + +/* Get size_t. */ +#include + +/* Get FILE. */ +#include + +extern char *fread_file (FILE * stream, size_t * length); + +extern char *read_file (const char *filename, size_t * length); + +extern char *read_binary_file (const char *filename, size_t * length); + +#endif /* READ_FILE_H */ diff --git a/src/daemon/https/lgl/realloc.c b/src/daemon/https/lgl/realloc.c new file mode 100644 index 00000000..4661f913 --- /dev/null +++ b/src/daemon/https/lgl/realloc.c @@ -0,0 +1,87 @@ +/* realloc() function that is glibc compatible. + + Copyright (C) 1997, 2003, 2004, 2006, 2007 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + This program 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . */ + +/* written by Jim Meyering and Bruno Haible */ + +#include + +/* Only the AC_FUNC_REALLOC macro defines 'realloc' already in config.h. */ +#ifdef realloc +# define NEED_REALLOC_GNU 1 +#endif + +/* Infer the properties of the system's malloc function. + Only the AC_FUNC_MALLOC macro defines 'malloc' already in config.h. */ +#if GNULIB_MALLOC_GNU && !defined malloc +# define SYSTEM_MALLOC_GLIBC_COMPATIBLE 1 +#endif + +/* Below we want to call the system's malloc and realloc. + Undefine the symbols here so that including provides a + declaration of malloc(), not of rpl_malloc(), and likewise for realloc. */ +#undef malloc +#undef realloc + +/* Specification. */ +#include + +#include + +/* Below we want to call the system's malloc and realloc. + Undefine the symbols, if they were defined by gnulib's + replacement. */ +#undef malloc +#undef realloc + +/* Change the size of an allocated block of memory P to N bytes, + with error checking. If N is zero, change it to 1. If P is NULL, + use malloc. */ + +void * +rpl_realloc (void *p, size_t n) +{ + void *result; + +#if NEED_REALLOC_GNU + if (n == 0) + { + n = 1; + + /* In theory realloc might fail, so don't rely on it to free. */ + free (p); + p = NULL; + } +#endif + + if (p == NULL) + { +#if GNULIB_REALLOC_GNU && !NEED_REALLOC_GNU && !SYSTEM_MALLOC_GLIBC_COMPATIBLE + if (n == 0) + n = 1; +#endif + result = malloc (n); + } + else + result = realloc (p, n); + +#if !HAVE_REALLOC_POSIX + if (result == NULL) + errno = ENOMEM; +#endif + + return result; +} diff --git a/src/daemon/https/lgl/rijndael-alg-fst.c b/src/daemon/https/lgl/rijndael-alg-fst.c new file mode 100644 index 00000000..5baa0e95 --- /dev/null +++ b/src/daemon/https/lgl/rijndael-alg-fst.c @@ -0,0 +1,1083 @@ +/* rijndael-alg-fst.c --- Rijndael cipher implementation. + * Copyright (C) 2005, 2006 Free Software Foundation, Inc. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2.1, or (at your + * option) any later version. + * + * This file 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 Lesser General Public License + * along with this file; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +/* Adapted for gnulib by Simon Josefsson. + * + * Based on public domain "Optimised C code" retrieved from (SHA1 + * 7c8e4b00d06685d1dbc6724a9e0d502353de339e): + * http://www.iaik.tu-graz.ac.at/research/krypto/AES/old/~rijmen/rijndael/rijndael-fst-3.0.zip + */ + +#include + +/** + * rijndael-alg-fst.c + * + * @version 3.0 (December 2000) + * + * Optimised ANSI C code for the Rijndael cipher (now AES) + * + * @author Vincent Rijmen + * @author Antoon Bosselaers + * @author Paulo Barreto + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "rijndael-alg-fst.h" + +/* +Te0[x] = S [x].[02, 01, 01, 03]; +Te1[x] = S [x].[03, 02, 01, 01]; +Te2[x] = S [x].[01, 03, 02, 01]; +Te3[x] = S [x].[01, 01, 03, 02]; +Te4[x] = S [x].[01, 01, 01, 01]; + +Td0[x] = Si[x].[0e, 09, 0d, 0b]; +Td1[x] = Si[x].[0b, 0e, 09, 0d]; +Td2[x] = Si[x].[0d, 0b, 0e, 09]; +Td3[x] = Si[x].[09, 0d, 0b, 0e]; +Td4[x] = Si[x].[01, 01, 01, 01]; +*/ + +static const uint32_t Te0[256] = { + 0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d, + 0xfff2f20d, 0xd66b6bbd, 0xde6f6fb1, 0x91c5c554, + 0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d, + 0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a, + 0x8fcaca45, 0x1f82829d, 0x89c9c940, 0xfa7d7d87, + 0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b, + 0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea, + 0x239c9cbf, 0x53a4a4f7, 0xe4727296, 0x9bc0c05b, + 0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a, + 0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f, + 0x6834345c, 0x51a5a5f4, 0xd1e5e534, 0xf9f1f108, + 0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f, + 0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e, + 0x30181828, 0x379696a1, 0x0a05050f, 0x2f9a9ab5, + 0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d, + 0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f, + 0x1209091b, 0x1d83839e, 0x582c2c74, 0x341a1a2e, + 0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb, + 0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce, + 0x5229297b, 0xdde3e33e, 0x5e2f2f71, 0x13848497, + 0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c, + 0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed, + 0xd46a6abe, 0x8dcbcb46, 0x67bebed9, 0x7239394b, + 0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a, + 0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16, + 0x864343c5, 0x9a4d4dd7, 0x66333355, 0x11858594, + 0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81, + 0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3, + 0xa25151f3, 0x5da3a3fe, 0x804040c0, 0x058f8f8a, + 0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504, + 0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163, + 0x20101030, 0xe5ffff1a, 0xfdf3f30e, 0xbfd2d26d, + 0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f, + 0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739, + 0x93c4c457, 0x55a7a7f2, 0xfc7e7e82, 0x7a3d3d47, + 0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395, + 0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f, + 0x44222266, 0x542a2a7e, 0x3b9090ab, 0x0b888883, + 0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c, + 0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76, + 0xdbe0e03b, 0x64323256, 0x743a3a4e, 0x140a0a1e, + 0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4, + 0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6, + 0x399191a8, 0x319595a4, 0xd3e4e437, 0xf279798b, + 0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7, + 0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0, + 0xd86c6cb4, 0xac5656fa, 0xf3f4f407, 0xcfeaea25, + 0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818, + 0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72, + 0x381c1c24, 0x57a6a6f1, 0x73b4b4c7, 0x97c6c651, + 0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21, + 0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85, + 0xe0707090, 0x7c3e3e42, 0x71b5b5c4, 0xcc6666aa, + 0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12, + 0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0, + 0x17868691, 0x99c1c158, 0x3a1d1d27, 0x279e9eb9, + 0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133, + 0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7, + 0x2d9b9bb6, 0x3c1e1e22, 0x15878792, 0xc9e9e920, + 0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a, + 0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17, + 0x65bfbfda, 0xd7e6e631, 0x844242c6, 0xd06868b8, + 0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11, + 0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a, +}; +static const uint32_t Te1[256] = { + 0xa5c66363, 0x84f87c7c, 0x99ee7777, 0x8df67b7b, + 0x0dfff2f2, 0xbdd66b6b, 0xb1de6f6f, 0x5491c5c5, + 0x50603030, 0x03020101, 0xa9ce6767, 0x7d562b2b, + 0x19e7fefe, 0x62b5d7d7, 0xe64dabab, 0x9aec7676, + 0x458fcaca, 0x9d1f8282, 0x4089c9c9, 0x87fa7d7d, + 0x15effafa, 0xebb25959, 0xc98e4747, 0x0bfbf0f0, + 0xec41adad, 0x67b3d4d4, 0xfd5fa2a2, 0xea45afaf, + 0xbf239c9c, 0xf753a4a4, 0x96e47272, 0x5b9bc0c0, + 0xc275b7b7, 0x1ce1fdfd, 0xae3d9393, 0x6a4c2626, + 0x5a6c3636, 0x417e3f3f, 0x02f5f7f7, 0x4f83cccc, + 0x5c683434, 0xf451a5a5, 0x34d1e5e5, 0x08f9f1f1, + 0x93e27171, 0x73abd8d8, 0x53623131, 0x3f2a1515, + 0x0c080404, 0x5295c7c7, 0x65462323, 0x5e9dc3c3, + 0x28301818, 0xa1379696, 0x0f0a0505, 0xb52f9a9a, + 0x090e0707, 0x36241212, 0x9b1b8080, 0x3ddfe2e2, + 0x26cdebeb, 0x694e2727, 0xcd7fb2b2, 0x9fea7575, + 0x1b120909, 0x9e1d8383, 0x74582c2c, 0x2e341a1a, + 0x2d361b1b, 0xb2dc6e6e, 0xeeb45a5a, 0xfb5ba0a0, + 0xf6a45252, 0x4d763b3b, 0x61b7d6d6, 0xce7db3b3, + 0x7b522929, 0x3edde3e3, 0x715e2f2f, 0x97138484, + 0xf5a65353, 0x68b9d1d1, 0x00000000, 0x2cc1eded, + 0x60402020, 0x1fe3fcfc, 0xc879b1b1, 0xedb65b5b, + 0xbed46a6a, 0x468dcbcb, 0xd967bebe, 0x4b723939, + 0xde944a4a, 0xd4984c4c, 0xe8b05858, 0x4a85cfcf, + 0x6bbbd0d0, 0x2ac5efef, 0xe54faaaa, 0x16edfbfb, + 0xc5864343, 0xd79a4d4d, 0x55663333, 0x94118585, + 0xcf8a4545, 0x10e9f9f9, 0x06040202, 0x81fe7f7f, + 0xf0a05050, 0x44783c3c, 0xba259f9f, 0xe34ba8a8, + 0xf3a25151, 0xfe5da3a3, 0xc0804040, 0x8a058f8f, + 0xad3f9292, 0xbc219d9d, 0x48703838, 0x04f1f5f5, + 0xdf63bcbc, 0xc177b6b6, 0x75afdada, 0x63422121, + 0x30201010, 0x1ae5ffff, 0x0efdf3f3, 0x6dbfd2d2, + 0x4c81cdcd, 0x14180c0c, 0x35261313, 0x2fc3ecec, + 0xe1be5f5f, 0xa2359797, 0xcc884444, 0x392e1717, + 0x5793c4c4, 0xf255a7a7, 0x82fc7e7e, 0x477a3d3d, + 0xacc86464, 0xe7ba5d5d, 0x2b321919, 0x95e67373, + 0xa0c06060, 0x98198181, 0xd19e4f4f, 0x7fa3dcdc, + 0x66442222, 0x7e542a2a, 0xab3b9090, 0x830b8888, + 0xca8c4646, 0x29c7eeee, 0xd36bb8b8, 0x3c281414, + 0x79a7dede, 0xe2bc5e5e, 0x1d160b0b, 0x76addbdb, + 0x3bdbe0e0, 0x56643232, 0x4e743a3a, 0x1e140a0a, + 0xdb924949, 0x0a0c0606, 0x6c482424, 0xe4b85c5c, + 0x5d9fc2c2, 0x6ebdd3d3, 0xef43acac, 0xa6c46262, + 0xa8399191, 0xa4319595, 0x37d3e4e4, 0x8bf27979, + 0x32d5e7e7, 0x438bc8c8, 0x596e3737, 0xb7da6d6d, + 0x8c018d8d, 0x64b1d5d5, 0xd29c4e4e, 0xe049a9a9, + 0xb4d86c6c, 0xfaac5656, 0x07f3f4f4, 0x25cfeaea, + 0xafca6565, 0x8ef47a7a, 0xe947aeae, 0x18100808, + 0xd56fbaba, 0x88f07878, 0x6f4a2525, 0x725c2e2e, + 0x24381c1c, 0xf157a6a6, 0xc773b4b4, 0x5197c6c6, + 0x23cbe8e8, 0x7ca1dddd, 0x9ce87474, 0x213e1f1f, + 0xdd964b4b, 0xdc61bdbd, 0x860d8b8b, 0x850f8a8a, + 0x90e07070, 0x427c3e3e, 0xc471b5b5, 0xaacc6666, + 0xd8904848, 0x05060303, 0x01f7f6f6, 0x121c0e0e, + 0xa3c26161, 0x5f6a3535, 0xf9ae5757, 0xd069b9b9, + 0x91178686, 0x5899c1c1, 0x273a1d1d, 0xb9279e9e, + 0x38d9e1e1, 0x13ebf8f8, 0xb32b9898, 0x33221111, + 0xbbd26969, 0x70a9d9d9, 0x89078e8e, 0xa7339494, + 0xb62d9b9b, 0x223c1e1e, 0x92158787, 0x20c9e9e9, + 0x4987cece, 0xffaa5555, 0x78502828, 0x7aa5dfdf, + 0x8f038c8c, 0xf859a1a1, 0x80098989, 0x171a0d0d, + 0xda65bfbf, 0x31d7e6e6, 0xc6844242, 0xb8d06868, + 0xc3824141, 0xb0299999, 0x775a2d2d, 0x111e0f0f, + 0xcb7bb0b0, 0xfca85454, 0xd66dbbbb, 0x3a2c1616, +}; +static const uint32_t Te2[256] = { + 0x63a5c663, 0x7c84f87c, 0x7799ee77, 0x7b8df67b, + 0xf20dfff2, 0x6bbdd66b, 0x6fb1de6f, 0xc55491c5, + 0x30506030, 0x01030201, 0x67a9ce67, 0x2b7d562b, + 0xfe19e7fe, 0xd762b5d7, 0xabe64dab, 0x769aec76, + 0xca458fca, 0x829d1f82, 0xc94089c9, 0x7d87fa7d, + 0xfa15effa, 0x59ebb259, 0x47c98e47, 0xf00bfbf0, + 0xadec41ad, 0xd467b3d4, 0xa2fd5fa2, 0xafea45af, + 0x9cbf239c, 0xa4f753a4, 0x7296e472, 0xc05b9bc0, + 0xb7c275b7, 0xfd1ce1fd, 0x93ae3d93, 0x266a4c26, + 0x365a6c36, 0x3f417e3f, 0xf702f5f7, 0xcc4f83cc, + 0x345c6834, 0xa5f451a5, 0xe534d1e5, 0xf108f9f1, + 0x7193e271, 0xd873abd8, 0x31536231, 0x153f2a15, + 0x040c0804, 0xc75295c7, 0x23654623, 0xc35e9dc3, + 0x18283018, 0x96a13796, 0x050f0a05, 0x9ab52f9a, + 0x07090e07, 0x12362412, 0x809b1b80, 0xe23ddfe2, + 0xeb26cdeb, 0x27694e27, 0xb2cd7fb2, 0x759fea75, + 0x091b1209, 0x839e1d83, 0x2c74582c, 0x1a2e341a, + 0x1b2d361b, 0x6eb2dc6e, 0x5aeeb45a, 0xa0fb5ba0, + 0x52f6a452, 0x3b4d763b, 0xd661b7d6, 0xb3ce7db3, + 0x297b5229, 0xe33edde3, 0x2f715e2f, 0x84971384, + 0x53f5a653, 0xd168b9d1, 0x00000000, 0xed2cc1ed, + 0x20604020, 0xfc1fe3fc, 0xb1c879b1, 0x5bedb65b, + 0x6abed46a, 0xcb468dcb, 0xbed967be, 0x394b7239, + 0x4ade944a, 0x4cd4984c, 0x58e8b058, 0xcf4a85cf, + 0xd06bbbd0, 0xef2ac5ef, 0xaae54faa, 0xfb16edfb, + 0x43c58643, 0x4dd79a4d, 0x33556633, 0x85941185, + 0x45cf8a45, 0xf910e9f9, 0x02060402, 0x7f81fe7f, + 0x50f0a050, 0x3c44783c, 0x9fba259f, 0xa8e34ba8, + 0x51f3a251, 0xa3fe5da3, 0x40c08040, 0x8f8a058f, + 0x92ad3f92, 0x9dbc219d, 0x38487038, 0xf504f1f5, + 0xbcdf63bc, 0xb6c177b6, 0xda75afda, 0x21634221, + 0x10302010, 0xff1ae5ff, 0xf30efdf3, 0xd26dbfd2, + 0xcd4c81cd, 0x0c14180c, 0x13352613, 0xec2fc3ec, + 0x5fe1be5f, 0x97a23597, 0x44cc8844, 0x17392e17, + 0xc45793c4, 0xa7f255a7, 0x7e82fc7e, 0x3d477a3d, + 0x64acc864, 0x5de7ba5d, 0x192b3219, 0x7395e673, + 0x60a0c060, 0x81981981, 0x4fd19e4f, 0xdc7fa3dc, + 0x22664422, 0x2a7e542a, 0x90ab3b90, 0x88830b88, + 0x46ca8c46, 0xee29c7ee, 0xb8d36bb8, 0x143c2814, + 0xde79a7de, 0x5ee2bc5e, 0x0b1d160b, 0xdb76addb, + 0xe03bdbe0, 0x32566432, 0x3a4e743a, 0x0a1e140a, + 0x49db9249, 0x060a0c06, 0x246c4824, 0x5ce4b85c, + 0xc25d9fc2, 0xd36ebdd3, 0xacef43ac, 0x62a6c462, + 0x91a83991, 0x95a43195, 0xe437d3e4, 0x798bf279, + 0xe732d5e7, 0xc8438bc8, 0x37596e37, 0x6db7da6d, + 0x8d8c018d, 0xd564b1d5, 0x4ed29c4e, 0xa9e049a9, + 0x6cb4d86c, 0x56faac56, 0xf407f3f4, 0xea25cfea, + 0x65afca65, 0x7a8ef47a, 0xaee947ae, 0x08181008, + 0xbad56fba, 0x7888f078, 0x256f4a25, 0x2e725c2e, + 0x1c24381c, 0xa6f157a6, 0xb4c773b4, 0xc65197c6, + 0xe823cbe8, 0xdd7ca1dd, 0x749ce874, 0x1f213e1f, + 0x4bdd964b, 0xbddc61bd, 0x8b860d8b, 0x8a850f8a, + 0x7090e070, 0x3e427c3e, 0xb5c471b5, 0x66aacc66, + 0x48d89048, 0x03050603, 0xf601f7f6, 0x0e121c0e, + 0x61a3c261, 0x355f6a35, 0x57f9ae57, 0xb9d069b9, + 0x86911786, 0xc15899c1, 0x1d273a1d, 0x9eb9279e, + 0xe138d9e1, 0xf813ebf8, 0x98b32b98, 0x11332211, + 0x69bbd269, 0xd970a9d9, 0x8e89078e, 0x94a73394, + 0x9bb62d9b, 0x1e223c1e, 0x87921587, 0xe920c9e9, + 0xce4987ce, 0x55ffaa55, 0x28785028, 0xdf7aa5df, + 0x8c8f038c, 0xa1f859a1, 0x89800989, 0x0d171a0d, + 0xbfda65bf, 0xe631d7e6, 0x42c68442, 0x68b8d068, + 0x41c38241, 0x99b02999, 0x2d775a2d, 0x0f111e0f, + 0xb0cb7bb0, 0x54fca854, 0xbbd66dbb, 0x163a2c16, +}; +static const uint32_t Te3[256] = { + 0x6363a5c6, 0x7c7c84f8, 0x777799ee, 0x7b7b8df6, + 0xf2f20dff, 0x6b6bbdd6, 0x6f6fb1de, 0xc5c55491, + 0x30305060, 0x01010302, 0x6767a9ce, 0x2b2b7d56, + 0xfefe19e7, 0xd7d762b5, 0xababe64d, 0x76769aec, + 0xcaca458f, 0x82829d1f, 0xc9c94089, 0x7d7d87fa, + 0xfafa15ef, 0x5959ebb2, 0x4747c98e, 0xf0f00bfb, + 0xadadec41, 0xd4d467b3, 0xa2a2fd5f, 0xafafea45, + 0x9c9cbf23, 0xa4a4f753, 0x727296e4, 0xc0c05b9b, + 0xb7b7c275, 0xfdfd1ce1, 0x9393ae3d, 0x26266a4c, + 0x36365a6c, 0x3f3f417e, 0xf7f702f5, 0xcccc4f83, + 0x34345c68, 0xa5a5f451, 0xe5e534d1, 0xf1f108f9, + 0x717193e2, 0xd8d873ab, 0x31315362, 0x15153f2a, + 0x04040c08, 0xc7c75295, 0x23236546, 0xc3c35e9d, + 0x18182830, 0x9696a137, 0x05050f0a, 0x9a9ab52f, + 0x0707090e, 0x12123624, 0x80809b1b, 0xe2e23ddf, + 0xebeb26cd, 0x2727694e, 0xb2b2cd7f, 0x75759fea, + 0x09091b12, 0x83839e1d, 0x2c2c7458, 0x1a1a2e34, + 0x1b1b2d36, 0x6e6eb2dc, 0x5a5aeeb4, 0xa0a0fb5b, + 0x5252f6a4, 0x3b3b4d76, 0xd6d661b7, 0xb3b3ce7d, + 0x29297b52, 0xe3e33edd, 0x2f2f715e, 0x84849713, + 0x5353f5a6, 0xd1d168b9, 0x00000000, 0xeded2cc1, + 0x20206040, 0xfcfc1fe3, 0xb1b1c879, 0x5b5bedb6, + 0x6a6abed4, 0xcbcb468d, 0xbebed967, 0x39394b72, + 0x4a4ade94, 0x4c4cd498, 0x5858e8b0, 0xcfcf4a85, + 0xd0d06bbb, 0xefef2ac5, 0xaaaae54f, 0xfbfb16ed, + 0x4343c586, 0x4d4dd79a, 0x33335566, 0x85859411, + 0x4545cf8a, 0xf9f910e9, 0x02020604, 0x7f7f81fe, + 0x5050f0a0, 0x3c3c4478, 0x9f9fba25, 0xa8a8e34b, + 0x5151f3a2, 0xa3a3fe5d, 0x4040c080, 0x8f8f8a05, + 0x9292ad3f, 0x9d9dbc21, 0x38384870, 0xf5f504f1, + 0xbcbcdf63, 0xb6b6c177, 0xdada75af, 0x21216342, + 0x10103020, 0xffff1ae5, 0xf3f30efd, 0xd2d26dbf, + 0xcdcd4c81, 0x0c0c1418, 0x13133526, 0xecec2fc3, + 0x5f5fe1be, 0x9797a235, 0x4444cc88, 0x1717392e, + 0xc4c45793, 0xa7a7f255, 0x7e7e82fc, 0x3d3d477a, + 0x6464acc8, 0x5d5de7ba, 0x19192b32, 0x737395e6, + 0x6060a0c0, 0x81819819, 0x4f4fd19e, 0xdcdc7fa3, + 0x22226644, 0x2a2a7e54, 0x9090ab3b, 0x8888830b, + 0x4646ca8c, 0xeeee29c7, 0xb8b8d36b, 0x14143c28, + 0xdede79a7, 0x5e5ee2bc, 0x0b0b1d16, 0xdbdb76ad, + 0xe0e03bdb, 0x32325664, 0x3a3a4e74, 0x0a0a1e14, + 0x4949db92, 0x06060a0c, 0x24246c48, 0x5c5ce4b8, + 0xc2c25d9f, 0xd3d36ebd, 0xacacef43, 0x6262a6c4, + 0x9191a839, 0x9595a431, 0xe4e437d3, 0x79798bf2, + 0xe7e732d5, 0xc8c8438b, 0x3737596e, 0x6d6db7da, + 0x8d8d8c01, 0xd5d564b1, 0x4e4ed29c, 0xa9a9e049, + 0x6c6cb4d8, 0x5656faac, 0xf4f407f3, 0xeaea25cf, + 0x6565afca, 0x7a7a8ef4, 0xaeaee947, 0x08081810, + 0xbabad56f, 0x787888f0, 0x25256f4a, 0x2e2e725c, + 0x1c1c2438, 0xa6a6f157, 0xb4b4c773, 0xc6c65197, + 0xe8e823cb, 0xdddd7ca1, 0x74749ce8, 0x1f1f213e, + 0x4b4bdd96, 0xbdbddc61, 0x8b8b860d, 0x8a8a850f, + 0x707090e0, 0x3e3e427c, 0xb5b5c471, 0x6666aacc, + 0x4848d890, 0x03030506, 0xf6f601f7, 0x0e0e121c, + 0x6161a3c2, 0x35355f6a, 0x5757f9ae, 0xb9b9d069, + 0x86869117, 0xc1c15899, 0x1d1d273a, 0x9e9eb927, + 0xe1e138d9, 0xf8f813eb, 0x9898b32b, 0x11113322, + 0x6969bbd2, 0xd9d970a9, 0x8e8e8907, 0x9494a733, + 0x9b9bb62d, 0x1e1e223c, 0x87879215, 0xe9e920c9, + 0xcece4987, 0x5555ffaa, 0x28287850, 0xdfdf7aa5, + 0x8c8c8f03, 0xa1a1f859, 0x89898009, 0x0d0d171a, + 0xbfbfda65, 0xe6e631d7, 0x4242c684, 0x6868b8d0, + 0x4141c382, 0x9999b029, 0x2d2d775a, 0x0f0f111e, + 0xb0b0cb7b, 0x5454fca8, 0xbbbbd66d, 0x16163a2c, +}; +static const uint32_t Te4[256] = { + 0x63636363, 0x7c7c7c7c, 0x77777777, 0x7b7b7b7b, + 0xf2f2f2f2, 0x6b6b6b6b, 0x6f6f6f6f, 0xc5c5c5c5, + 0x30303030, 0x01010101, 0x67676767, 0x2b2b2b2b, + 0xfefefefe, 0xd7d7d7d7, 0xabababab, 0x76767676, + 0xcacacaca, 0x82828282, 0xc9c9c9c9, 0x7d7d7d7d, + 0xfafafafa, 0x59595959, 0x47474747, 0xf0f0f0f0, + 0xadadadad, 0xd4d4d4d4, 0xa2a2a2a2, 0xafafafaf, + 0x9c9c9c9c, 0xa4a4a4a4, 0x72727272, 0xc0c0c0c0, + 0xb7b7b7b7, 0xfdfdfdfd, 0x93939393, 0x26262626, + 0x36363636, 0x3f3f3f3f, 0xf7f7f7f7, 0xcccccccc, + 0x34343434, 0xa5a5a5a5, 0xe5e5e5e5, 0xf1f1f1f1, + 0x71717171, 0xd8d8d8d8, 0x31313131, 0x15151515, + 0x04040404, 0xc7c7c7c7, 0x23232323, 0xc3c3c3c3, + 0x18181818, 0x96969696, 0x05050505, 0x9a9a9a9a, + 0x07070707, 0x12121212, 0x80808080, 0xe2e2e2e2, + 0xebebebeb, 0x27272727, 0xb2b2b2b2, 0x75757575, + 0x09090909, 0x83838383, 0x2c2c2c2c, 0x1a1a1a1a, + 0x1b1b1b1b, 0x6e6e6e6e, 0x5a5a5a5a, 0xa0a0a0a0, + 0x52525252, 0x3b3b3b3b, 0xd6d6d6d6, 0xb3b3b3b3, + 0x29292929, 0xe3e3e3e3, 0x2f2f2f2f, 0x84848484, + 0x53535353, 0xd1d1d1d1, 0x00000000, 0xedededed, + 0x20202020, 0xfcfcfcfc, 0xb1b1b1b1, 0x5b5b5b5b, + 0x6a6a6a6a, 0xcbcbcbcb, 0xbebebebe, 0x39393939, + 0x4a4a4a4a, 0x4c4c4c4c, 0x58585858, 0xcfcfcfcf, + 0xd0d0d0d0, 0xefefefef, 0xaaaaaaaa, 0xfbfbfbfb, + 0x43434343, 0x4d4d4d4d, 0x33333333, 0x85858585, + 0x45454545, 0xf9f9f9f9, 0x02020202, 0x7f7f7f7f, + 0x50505050, 0x3c3c3c3c, 0x9f9f9f9f, 0xa8a8a8a8, + 0x51515151, 0xa3a3a3a3, 0x40404040, 0x8f8f8f8f, + 0x92929292, 0x9d9d9d9d, 0x38383838, 0xf5f5f5f5, + 0xbcbcbcbc, 0xb6b6b6b6, 0xdadadada, 0x21212121, + 0x10101010, 0xffffffff, 0xf3f3f3f3, 0xd2d2d2d2, + 0xcdcdcdcd, 0x0c0c0c0c, 0x13131313, 0xecececec, + 0x5f5f5f5f, 0x97979797, 0x44444444, 0x17171717, + 0xc4c4c4c4, 0xa7a7a7a7, 0x7e7e7e7e, 0x3d3d3d3d, + 0x64646464, 0x5d5d5d5d, 0x19191919, 0x73737373, + 0x60606060, 0x81818181, 0x4f4f4f4f, 0xdcdcdcdc, + 0x22222222, 0x2a2a2a2a, 0x90909090, 0x88888888, + 0x46464646, 0xeeeeeeee, 0xb8b8b8b8, 0x14141414, + 0xdededede, 0x5e5e5e5e, 0x0b0b0b0b, 0xdbdbdbdb, + 0xe0e0e0e0, 0x32323232, 0x3a3a3a3a, 0x0a0a0a0a, + 0x49494949, 0x06060606, 0x24242424, 0x5c5c5c5c, + 0xc2c2c2c2, 0xd3d3d3d3, 0xacacacac, 0x62626262, + 0x91919191, 0x95959595, 0xe4e4e4e4, 0x79797979, + 0xe7e7e7e7, 0xc8c8c8c8, 0x37373737, 0x6d6d6d6d, + 0x8d8d8d8d, 0xd5d5d5d5, 0x4e4e4e4e, 0xa9a9a9a9, + 0x6c6c6c6c, 0x56565656, 0xf4f4f4f4, 0xeaeaeaea, + 0x65656565, 0x7a7a7a7a, 0xaeaeaeae, 0x08080808, + 0xbabababa, 0x78787878, 0x25252525, 0x2e2e2e2e, + 0x1c1c1c1c, 0xa6a6a6a6, 0xb4b4b4b4, 0xc6c6c6c6, + 0xe8e8e8e8, 0xdddddddd, 0x74747474, 0x1f1f1f1f, + 0x4b4b4b4b, 0xbdbdbdbd, 0x8b8b8b8b, 0x8a8a8a8a, + 0x70707070, 0x3e3e3e3e, 0xb5b5b5b5, 0x66666666, + 0x48484848, 0x03030303, 0xf6f6f6f6, 0x0e0e0e0e, + 0x61616161, 0x35353535, 0x57575757, 0xb9b9b9b9, + 0x86868686, 0xc1c1c1c1, 0x1d1d1d1d, 0x9e9e9e9e, + 0xe1e1e1e1, 0xf8f8f8f8, 0x98989898, 0x11111111, + 0x69696969, 0xd9d9d9d9, 0x8e8e8e8e, 0x94949494, + 0x9b9b9b9b, 0x1e1e1e1e, 0x87878787, 0xe9e9e9e9, + 0xcececece, 0x55555555, 0x28282828, 0xdfdfdfdf, + 0x8c8c8c8c, 0xa1a1a1a1, 0x89898989, 0x0d0d0d0d, + 0xbfbfbfbf, 0xe6e6e6e6, 0x42424242, 0x68686868, + 0x41414141, 0x99999999, 0x2d2d2d2d, 0x0f0f0f0f, + 0xb0b0b0b0, 0x54545454, 0xbbbbbbbb, 0x16161616, +}; +static const uint32_t Td0[256] = { + 0x51f4a750, 0x7e416553, 0x1a17a4c3, 0x3a275e96, + 0x3bab6bcb, 0x1f9d45f1, 0xacfa58ab, 0x4be30393, + 0x2030fa55, 0xad766df6, 0x88cc7691, 0xf5024c25, + 0x4fe5d7fc, 0xc52acbd7, 0x26354480, 0xb562a38f, + 0xdeb15a49, 0x25ba1b67, 0x45ea0e98, 0x5dfec0e1, + 0xc32f7502, 0x814cf012, 0x8d4697a3, 0x6bd3f9c6, + 0x038f5fe7, 0x15929c95, 0xbf6d7aeb, 0x955259da, + 0xd4be832d, 0x587421d3, 0x49e06929, 0x8ec9c844, + 0x75c2896a, 0xf48e7978, 0x99583e6b, 0x27b971dd, + 0xbee14fb6, 0xf088ad17, 0xc920ac66, 0x7dce3ab4, + 0x63df4a18, 0xe51a3182, 0x97513360, 0x62537f45, + 0xb16477e0, 0xbb6bae84, 0xfe81a01c, 0xf9082b94, + 0x70486858, 0x8f45fd19, 0x94de6c87, 0x527bf8b7, + 0xab73d323, 0x724b02e2, 0xe31f8f57, 0x6655ab2a, + 0xb2eb2807, 0x2fb5c203, 0x86c57b9a, 0xd33708a5, + 0x302887f2, 0x23bfa5b2, 0x02036aba, 0xed16825c, + 0x8acf1c2b, 0xa779b492, 0xf307f2f0, 0x4e69e2a1, + 0x65daf4cd, 0x0605bed5, 0xd134621f, 0xc4a6fe8a, + 0x342e539d, 0xa2f355a0, 0x058ae132, 0xa4f6eb75, + 0x0b83ec39, 0x4060efaa, 0x5e719f06, 0xbd6e1051, + 0x3e218af9, 0x96dd063d, 0xdd3e05ae, 0x4de6bd46, + 0x91548db5, 0x71c45d05, 0x0406d46f, 0x605015ff, + 0x1998fb24, 0xd6bde997, 0x894043cc, 0x67d99e77, + 0xb0e842bd, 0x07898b88, 0xe7195b38, 0x79c8eedb, + 0xa17c0a47, 0x7c420fe9, 0xf8841ec9, 0x00000000, + 0x09808683, 0x322bed48, 0x1e1170ac, 0x6c5a724e, + 0xfd0efffb, 0x0f853856, 0x3daed51e, 0x362d3927, + 0x0a0fd964, 0x685ca621, 0x9b5b54d1, 0x24362e3a, + 0x0c0a67b1, 0x9357e70f, 0xb4ee96d2, 0x1b9b919e, + 0x80c0c54f, 0x61dc20a2, 0x5a774b69, 0x1c121a16, + 0xe293ba0a, 0xc0a02ae5, 0x3c22e043, 0x121b171d, + 0x0e090d0b, 0xf28bc7ad, 0x2db6a8b9, 0x141ea9c8, + 0x57f11985, 0xaf75074c, 0xee99ddbb, 0xa37f60fd, + 0xf701269f, 0x5c72f5bc, 0x44663bc5, 0x5bfb7e34, + 0x8b432976, 0xcb23c6dc, 0xb6edfc68, 0xb8e4f163, + 0xd731dcca, 0x42638510, 0x13972240, 0x84c61120, + 0x854a247d, 0xd2bb3df8, 0xaef93211, 0xc729a16d, + 0x1d9e2f4b, 0xdcb230f3, 0x0d8652ec, 0x77c1e3d0, + 0x2bb3166c, 0xa970b999, 0x119448fa, 0x47e96422, + 0xa8fc8cc4, 0xa0f03f1a, 0x567d2cd8, 0x223390ef, + 0x87494ec7, 0xd938d1c1, 0x8ccaa2fe, 0x98d40b36, + 0xa6f581cf, 0xa57ade28, 0xdab78e26, 0x3fadbfa4, + 0x2c3a9de4, 0x5078920d, 0x6a5fcc9b, 0x547e4662, + 0xf68d13c2, 0x90d8b8e8, 0x2e39f75e, 0x82c3aff5, + 0x9f5d80be, 0x69d0937c, 0x6fd52da9, 0xcf2512b3, + 0xc8ac993b, 0x10187da7, 0xe89c636e, 0xdb3bbb7b, + 0xcd267809, 0x6e5918f4, 0xec9ab701, 0x834f9aa8, + 0xe6956e65, 0xaaffe67e, 0x21bccf08, 0xef15e8e6, + 0xbae79bd9, 0x4a6f36ce, 0xea9f09d4, 0x29b07cd6, + 0x31a4b2af, 0x2a3f2331, 0xc6a59430, 0x35a266c0, + 0x744ebc37, 0xfc82caa6, 0xe090d0b0, 0x33a7d815, + 0xf104984a, 0x41ecdaf7, 0x7fcd500e, 0x1791f62f, + 0x764dd68d, 0x43efb04d, 0xccaa4d54, 0xe49604df, + 0x9ed1b5e3, 0x4c6a881b, 0xc12c1fb8, 0x4665517f, + 0x9d5eea04, 0x018c355d, 0xfa877473, 0xfb0b412e, + 0xb3671d5a, 0x92dbd252, 0xe9105633, 0x6dd64713, + 0x9ad7618c, 0x37a10c7a, 0x59f8148e, 0xeb133c89, + 0xcea927ee, 0xb761c935, 0xe11ce5ed, 0x7a47b13c, + 0x9cd2df59, 0x55f2733f, 0x1814ce79, 0x73c737bf, + 0x53f7cdea, 0x5ffdaa5b, 0xdf3d6f14, 0x7844db86, + 0xcaaff381, 0xb968c43e, 0x3824342c, 0xc2a3405f, + 0x161dc372, 0xbce2250c, 0x283c498b, 0xff0d9541, + 0x39a80171, 0x080cb3de, 0xd8b4e49c, 0x6456c190, + 0x7bcb8461, 0xd532b670, 0x486c5c74, 0xd0b85742, +}; +static const uint32_t Td1[256] = { + 0x5051f4a7, 0x537e4165, 0xc31a17a4, 0x963a275e, + 0xcb3bab6b, 0xf11f9d45, 0xabacfa58, 0x934be303, + 0x552030fa, 0xf6ad766d, 0x9188cc76, 0x25f5024c, + 0xfc4fe5d7, 0xd7c52acb, 0x80263544, 0x8fb562a3, + 0x49deb15a, 0x6725ba1b, 0x9845ea0e, 0xe15dfec0, + 0x02c32f75, 0x12814cf0, 0xa38d4697, 0xc66bd3f9, + 0xe7038f5f, 0x9515929c, 0xebbf6d7a, 0xda955259, + 0x2dd4be83, 0xd3587421, 0x2949e069, 0x448ec9c8, + 0x6a75c289, 0x78f48e79, 0x6b99583e, 0xdd27b971, + 0xb6bee14f, 0x17f088ad, 0x66c920ac, 0xb47dce3a, + 0x1863df4a, 0x82e51a31, 0x60975133, 0x4562537f, + 0xe0b16477, 0x84bb6bae, 0x1cfe81a0, 0x94f9082b, + 0x58704868, 0x198f45fd, 0x8794de6c, 0xb7527bf8, + 0x23ab73d3, 0xe2724b02, 0x57e31f8f, 0x2a6655ab, + 0x07b2eb28, 0x032fb5c2, 0x9a86c57b, 0xa5d33708, + 0xf2302887, 0xb223bfa5, 0xba02036a, 0x5ced1682, + 0x2b8acf1c, 0x92a779b4, 0xf0f307f2, 0xa14e69e2, + 0xcd65daf4, 0xd50605be, 0x1fd13462, 0x8ac4a6fe, + 0x9d342e53, 0xa0a2f355, 0x32058ae1, 0x75a4f6eb, + 0x390b83ec, 0xaa4060ef, 0x065e719f, 0x51bd6e10, + 0xf93e218a, 0x3d96dd06, 0xaedd3e05, 0x464de6bd, + 0xb591548d, 0x0571c45d, 0x6f0406d4, 0xff605015, + 0x241998fb, 0x97d6bde9, 0xcc894043, 0x7767d99e, + 0xbdb0e842, 0x8807898b, 0x38e7195b, 0xdb79c8ee, + 0x47a17c0a, 0xe97c420f, 0xc9f8841e, 0x00000000, + 0x83098086, 0x48322bed, 0xac1e1170, 0x4e6c5a72, + 0xfbfd0eff, 0x560f8538, 0x1e3daed5, 0x27362d39, + 0x640a0fd9, 0x21685ca6, 0xd19b5b54, 0x3a24362e, + 0xb10c0a67, 0x0f9357e7, 0xd2b4ee96, 0x9e1b9b91, + 0x4f80c0c5, 0xa261dc20, 0x695a774b, 0x161c121a, + 0x0ae293ba, 0xe5c0a02a, 0x433c22e0, 0x1d121b17, + 0x0b0e090d, 0xadf28bc7, 0xb92db6a8, 0xc8141ea9, + 0x8557f119, 0x4caf7507, 0xbbee99dd, 0xfda37f60, + 0x9ff70126, 0xbc5c72f5, 0xc544663b, 0x345bfb7e, + 0x768b4329, 0xdccb23c6, 0x68b6edfc, 0x63b8e4f1, + 0xcad731dc, 0x10426385, 0x40139722, 0x2084c611, + 0x7d854a24, 0xf8d2bb3d, 0x11aef932, 0x6dc729a1, + 0x4b1d9e2f, 0xf3dcb230, 0xec0d8652, 0xd077c1e3, + 0x6c2bb316, 0x99a970b9, 0xfa119448, 0x2247e964, + 0xc4a8fc8c, 0x1aa0f03f, 0xd8567d2c, 0xef223390, + 0xc787494e, 0xc1d938d1, 0xfe8ccaa2, 0x3698d40b, + 0xcfa6f581, 0x28a57ade, 0x26dab78e, 0xa43fadbf, + 0xe42c3a9d, 0x0d507892, 0x9b6a5fcc, 0x62547e46, + 0xc2f68d13, 0xe890d8b8, 0x5e2e39f7, 0xf582c3af, + 0xbe9f5d80, 0x7c69d093, 0xa96fd52d, 0xb3cf2512, + 0x3bc8ac99, 0xa710187d, 0x6ee89c63, 0x7bdb3bbb, + 0x09cd2678, 0xf46e5918, 0x01ec9ab7, 0xa8834f9a, + 0x65e6956e, 0x7eaaffe6, 0x0821bccf, 0xe6ef15e8, + 0xd9bae79b, 0xce4a6f36, 0xd4ea9f09, 0xd629b07c, + 0xaf31a4b2, 0x312a3f23, 0x30c6a594, 0xc035a266, + 0x37744ebc, 0xa6fc82ca, 0xb0e090d0, 0x1533a7d8, + 0x4af10498, 0xf741ecda, 0x0e7fcd50, 0x2f1791f6, + 0x8d764dd6, 0x4d43efb0, 0x54ccaa4d, 0xdfe49604, + 0xe39ed1b5, 0x1b4c6a88, 0xb8c12c1f, 0x7f466551, + 0x049d5eea, 0x5d018c35, 0x73fa8774, 0x2efb0b41, + 0x5ab3671d, 0x5292dbd2, 0x33e91056, 0x136dd647, + 0x8c9ad761, 0x7a37a10c, 0x8e59f814, 0x89eb133c, + 0xeecea927, 0x35b761c9, 0xede11ce5, 0x3c7a47b1, + 0x599cd2df, 0x3f55f273, 0x791814ce, 0xbf73c737, + 0xea53f7cd, 0x5b5ffdaa, 0x14df3d6f, 0x867844db, + 0x81caaff3, 0x3eb968c4, 0x2c382434, 0x5fc2a340, + 0x72161dc3, 0x0cbce225, 0x8b283c49, 0x41ff0d95, + 0x7139a801, 0xde080cb3, 0x9cd8b4e4, 0x906456c1, + 0x617bcb84, 0x70d532b6, 0x74486c5c, 0x42d0b857, +}; +static const uint32_t Td2[256] = { + 0xa75051f4, 0x65537e41, 0xa4c31a17, 0x5e963a27, + 0x6bcb3bab, 0x45f11f9d, 0x58abacfa, 0x03934be3, + 0xfa552030, 0x6df6ad76, 0x769188cc, 0x4c25f502, + 0xd7fc4fe5, 0xcbd7c52a, 0x44802635, 0xa38fb562, + 0x5a49deb1, 0x1b6725ba, 0x0e9845ea, 0xc0e15dfe, + 0x7502c32f, 0xf012814c, 0x97a38d46, 0xf9c66bd3, + 0x5fe7038f, 0x9c951592, 0x7aebbf6d, 0x59da9552, + 0x832dd4be, 0x21d35874, 0x692949e0, 0xc8448ec9, + 0x896a75c2, 0x7978f48e, 0x3e6b9958, 0x71dd27b9, + 0x4fb6bee1, 0xad17f088, 0xac66c920, 0x3ab47dce, + 0x4a1863df, 0x3182e51a, 0x33609751, 0x7f456253, + 0x77e0b164, 0xae84bb6b, 0xa01cfe81, 0x2b94f908, + 0x68587048, 0xfd198f45, 0x6c8794de, 0xf8b7527b, + 0xd323ab73, 0x02e2724b, 0x8f57e31f, 0xab2a6655, + 0x2807b2eb, 0xc2032fb5, 0x7b9a86c5, 0x08a5d337, + 0x87f23028, 0xa5b223bf, 0x6aba0203, 0x825ced16, + 0x1c2b8acf, 0xb492a779, 0xf2f0f307, 0xe2a14e69, + 0xf4cd65da, 0xbed50605, 0x621fd134, 0xfe8ac4a6, + 0x539d342e, 0x55a0a2f3, 0xe132058a, 0xeb75a4f6, + 0xec390b83, 0xefaa4060, 0x9f065e71, 0x1051bd6e, + 0x8af93e21, 0x063d96dd, 0x05aedd3e, 0xbd464de6, + 0x8db59154, 0x5d0571c4, 0xd46f0406, 0x15ff6050, + 0xfb241998, 0xe997d6bd, 0x43cc8940, 0x9e7767d9, + 0x42bdb0e8, 0x8b880789, 0x5b38e719, 0xeedb79c8, + 0x0a47a17c, 0x0fe97c42, 0x1ec9f884, 0x00000000, + 0x86830980, 0xed48322b, 0x70ac1e11, 0x724e6c5a, + 0xfffbfd0e, 0x38560f85, 0xd51e3dae, 0x3927362d, + 0xd9640a0f, 0xa621685c, 0x54d19b5b, 0x2e3a2436, + 0x67b10c0a, 0xe70f9357, 0x96d2b4ee, 0x919e1b9b, + 0xc54f80c0, 0x20a261dc, 0x4b695a77, 0x1a161c12, + 0xba0ae293, 0x2ae5c0a0, 0xe0433c22, 0x171d121b, + 0x0d0b0e09, 0xc7adf28b, 0xa8b92db6, 0xa9c8141e, + 0x198557f1, 0x074caf75, 0xddbbee99, 0x60fda37f, + 0x269ff701, 0xf5bc5c72, 0x3bc54466, 0x7e345bfb, + 0x29768b43, 0xc6dccb23, 0xfc68b6ed, 0xf163b8e4, + 0xdccad731, 0x85104263, 0x22401397, 0x112084c6, + 0x247d854a, 0x3df8d2bb, 0x3211aef9, 0xa16dc729, + 0x2f4b1d9e, 0x30f3dcb2, 0x52ec0d86, 0xe3d077c1, + 0x166c2bb3, 0xb999a970, 0x48fa1194, 0x642247e9, + 0x8cc4a8fc, 0x3f1aa0f0, 0x2cd8567d, 0x90ef2233, + 0x4ec78749, 0xd1c1d938, 0xa2fe8cca, 0x0b3698d4, + 0x81cfa6f5, 0xde28a57a, 0x8e26dab7, 0xbfa43fad, + 0x9de42c3a, 0x920d5078, 0xcc9b6a5f, 0x4662547e, + 0x13c2f68d, 0xb8e890d8, 0xf75e2e39, 0xaff582c3, + 0x80be9f5d, 0x937c69d0, 0x2da96fd5, 0x12b3cf25, + 0x993bc8ac, 0x7da71018, 0x636ee89c, 0xbb7bdb3b, + 0x7809cd26, 0x18f46e59, 0xb701ec9a, 0x9aa8834f, + 0x6e65e695, 0xe67eaaff, 0xcf0821bc, 0xe8e6ef15, + 0x9bd9bae7, 0x36ce4a6f, 0x09d4ea9f, 0x7cd629b0, + 0xb2af31a4, 0x23312a3f, 0x9430c6a5, 0x66c035a2, + 0xbc37744e, 0xcaa6fc82, 0xd0b0e090, 0xd81533a7, + 0x984af104, 0xdaf741ec, 0x500e7fcd, 0xf62f1791, + 0xd68d764d, 0xb04d43ef, 0x4d54ccaa, 0x04dfe496, + 0xb5e39ed1, 0x881b4c6a, 0x1fb8c12c, 0x517f4665, + 0xea049d5e, 0x355d018c, 0x7473fa87, 0x412efb0b, + 0x1d5ab367, 0xd25292db, 0x5633e910, 0x47136dd6, + 0x618c9ad7, 0x0c7a37a1, 0x148e59f8, 0x3c89eb13, + 0x27eecea9, 0xc935b761, 0xe5ede11c, 0xb13c7a47, + 0xdf599cd2, 0x733f55f2, 0xce791814, 0x37bf73c7, + 0xcdea53f7, 0xaa5b5ffd, 0x6f14df3d, 0xdb867844, + 0xf381caaf, 0xc43eb968, 0x342c3824, 0x405fc2a3, + 0xc372161d, 0x250cbce2, 0x498b283c, 0x9541ff0d, + 0x017139a8, 0xb3de080c, 0xe49cd8b4, 0xc1906456, + 0x84617bcb, 0xb670d532, 0x5c74486c, 0x5742d0b8, +}; +static const uint32_t Td3[256] = { + 0xf4a75051, 0x4165537e, 0x17a4c31a, 0x275e963a, + 0xab6bcb3b, 0x9d45f11f, 0xfa58abac, 0xe303934b, + 0x30fa5520, 0x766df6ad, 0xcc769188, 0x024c25f5, + 0xe5d7fc4f, 0x2acbd7c5, 0x35448026, 0x62a38fb5, + 0xb15a49de, 0xba1b6725, 0xea0e9845, 0xfec0e15d, + 0x2f7502c3, 0x4cf01281, 0x4697a38d, 0xd3f9c66b, + 0x8f5fe703, 0x929c9515, 0x6d7aebbf, 0x5259da95, + 0xbe832dd4, 0x7421d358, 0xe0692949, 0xc9c8448e, + 0xc2896a75, 0x8e7978f4, 0x583e6b99, 0xb971dd27, + 0xe14fb6be, 0x88ad17f0, 0x20ac66c9, 0xce3ab47d, + 0xdf4a1863, 0x1a3182e5, 0x51336097, 0x537f4562, + 0x6477e0b1, 0x6bae84bb, 0x81a01cfe, 0x082b94f9, + 0x48685870, 0x45fd198f, 0xde6c8794, 0x7bf8b752, + 0x73d323ab, 0x4b02e272, 0x1f8f57e3, 0x55ab2a66, + 0xeb2807b2, 0xb5c2032f, 0xc57b9a86, 0x3708a5d3, + 0x2887f230, 0xbfa5b223, 0x036aba02, 0x16825ced, + 0xcf1c2b8a, 0x79b492a7, 0x07f2f0f3, 0x69e2a14e, + 0xdaf4cd65, 0x05bed506, 0x34621fd1, 0xa6fe8ac4, + 0x2e539d34, 0xf355a0a2, 0x8ae13205, 0xf6eb75a4, + 0x83ec390b, 0x60efaa40, 0x719f065e, 0x6e1051bd, + 0x218af93e, 0xdd063d96, 0x3e05aedd, 0xe6bd464d, + 0x548db591, 0xc45d0571, 0x06d46f04, 0x5015ff60, + 0x98fb2419, 0xbde997d6, 0x4043cc89, 0xd99e7767, + 0xe842bdb0, 0x898b8807, 0x195b38e7, 0xc8eedb79, + 0x7c0a47a1, 0x420fe97c, 0x841ec9f8, 0x00000000, + 0x80868309, 0x2bed4832, 0x1170ac1e, 0x5a724e6c, + 0x0efffbfd, 0x8538560f, 0xaed51e3d, 0x2d392736, + 0x0fd9640a, 0x5ca62168, 0x5b54d19b, 0x362e3a24, + 0x0a67b10c, 0x57e70f93, 0xee96d2b4, 0x9b919e1b, + 0xc0c54f80, 0xdc20a261, 0x774b695a, 0x121a161c, + 0x93ba0ae2, 0xa02ae5c0, 0x22e0433c, 0x1b171d12, + 0x090d0b0e, 0x8bc7adf2, 0xb6a8b92d, 0x1ea9c814, + 0xf1198557, 0x75074caf, 0x99ddbbee, 0x7f60fda3, + 0x01269ff7, 0x72f5bc5c, 0x663bc544, 0xfb7e345b, + 0x4329768b, 0x23c6dccb, 0xedfc68b6, 0xe4f163b8, + 0x31dccad7, 0x63851042, 0x97224013, 0xc6112084, + 0x4a247d85, 0xbb3df8d2, 0xf93211ae, 0x29a16dc7, + 0x9e2f4b1d, 0xb230f3dc, 0x8652ec0d, 0xc1e3d077, + 0xb3166c2b, 0x70b999a9, 0x9448fa11, 0xe9642247, + 0xfc8cc4a8, 0xf03f1aa0, 0x7d2cd856, 0x3390ef22, + 0x494ec787, 0x38d1c1d9, 0xcaa2fe8c, 0xd40b3698, + 0xf581cfa6, 0x7ade28a5, 0xb78e26da, 0xadbfa43f, + 0x3a9de42c, 0x78920d50, 0x5fcc9b6a, 0x7e466254, + 0x8d13c2f6, 0xd8b8e890, 0x39f75e2e, 0xc3aff582, + 0x5d80be9f, 0xd0937c69, 0xd52da96f, 0x2512b3cf, + 0xac993bc8, 0x187da710, 0x9c636ee8, 0x3bbb7bdb, + 0x267809cd, 0x5918f46e, 0x9ab701ec, 0x4f9aa883, + 0x956e65e6, 0xffe67eaa, 0xbccf0821, 0x15e8e6ef, + 0xe79bd9ba, 0x6f36ce4a, 0x9f09d4ea, 0xb07cd629, + 0xa4b2af31, 0x3f23312a, 0xa59430c6, 0xa266c035, + 0x4ebc3774, 0x82caa6fc, 0x90d0b0e0, 0xa7d81533, + 0x04984af1, 0xecdaf741, 0xcd500e7f, 0x91f62f17, + 0x4dd68d76, 0xefb04d43, 0xaa4d54cc, 0x9604dfe4, + 0xd1b5e39e, 0x6a881b4c, 0x2c1fb8c1, 0x65517f46, + 0x5eea049d, 0x8c355d01, 0x877473fa, 0x0b412efb, + 0x671d5ab3, 0xdbd25292, 0x105633e9, 0xd647136d, + 0xd7618c9a, 0xa10c7a37, 0xf8148e59, 0x133c89eb, + 0xa927eece, 0x61c935b7, 0x1ce5ede1, 0x47b13c7a, + 0xd2df599c, 0xf2733f55, 0x14ce7918, 0xc737bf73, + 0xf7cdea53, 0xfdaa5b5f, 0x3d6f14df, 0x44db8678, + 0xaff381ca, 0x68c43eb9, 0x24342c38, 0xa3405fc2, + 0x1dc37216, 0xe2250cbc, 0x3c498b28, 0x0d9541ff, + 0xa8017139, 0x0cb3de08, 0xb4e49cd8, 0x56c19064, + 0xcb84617b, 0x32b670d5, 0x6c5c7448, 0xb85742d0, +}; +static const uint32_t Td4[256] = { + 0x52525252, 0x09090909, 0x6a6a6a6a, 0xd5d5d5d5, + 0x30303030, 0x36363636, 0xa5a5a5a5, 0x38383838, + 0xbfbfbfbf, 0x40404040, 0xa3a3a3a3, 0x9e9e9e9e, + 0x81818181, 0xf3f3f3f3, 0xd7d7d7d7, 0xfbfbfbfb, + 0x7c7c7c7c, 0xe3e3e3e3, 0x39393939, 0x82828282, + 0x9b9b9b9b, 0x2f2f2f2f, 0xffffffff, 0x87878787, + 0x34343434, 0x8e8e8e8e, 0x43434343, 0x44444444, + 0xc4c4c4c4, 0xdededede, 0xe9e9e9e9, 0xcbcbcbcb, + 0x54545454, 0x7b7b7b7b, 0x94949494, 0x32323232, + 0xa6a6a6a6, 0xc2c2c2c2, 0x23232323, 0x3d3d3d3d, + 0xeeeeeeee, 0x4c4c4c4c, 0x95959595, 0x0b0b0b0b, + 0x42424242, 0xfafafafa, 0xc3c3c3c3, 0x4e4e4e4e, + 0x08080808, 0x2e2e2e2e, 0xa1a1a1a1, 0x66666666, + 0x28282828, 0xd9d9d9d9, 0x24242424, 0xb2b2b2b2, + 0x76767676, 0x5b5b5b5b, 0xa2a2a2a2, 0x49494949, + 0x6d6d6d6d, 0x8b8b8b8b, 0xd1d1d1d1, 0x25252525, + 0x72727272, 0xf8f8f8f8, 0xf6f6f6f6, 0x64646464, + 0x86868686, 0x68686868, 0x98989898, 0x16161616, + 0xd4d4d4d4, 0xa4a4a4a4, 0x5c5c5c5c, 0xcccccccc, + 0x5d5d5d5d, 0x65656565, 0xb6b6b6b6, 0x92929292, + 0x6c6c6c6c, 0x70707070, 0x48484848, 0x50505050, + 0xfdfdfdfd, 0xedededed, 0xb9b9b9b9, 0xdadadada, + 0x5e5e5e5e, 0x15151515, 0x46464646, 0x57575757, + 0xa7a7a7a7, 0x8d8d8d8d, 0x9d9d9d9d, 0x84848484, + 0x90909090, 0xd8d8d8d8, 0xabababab, 0x00000000, + 0x8c8c8c8c, 0xbcbcbcbc, 0xd3d3d3d3, 0x0a0a0a0a, + 0xf7f7f7f7, 0xe4e4e4e4, 0x58585858, 0x05050505, + 0xb8b8b8b8, 0xb3b3b3b3, 0x45454545, 0x06060606, + 0xd0d0d0d0, 0x2c2c2c2c, 0x1e1e1e1e, 0x8f8f8f8f, + 0xcacacaca, 0x3f3f3f3f, 0x0f0f0f0f, 0x02020202, + 0xc1c1c1c1, 0xafafafaf, 0xbdbdbdbd, 0x03030303, + 0x01010101, 0x13131313, 0x8a8a8a8a, 0x6b6b6b6b, + 0x3a3a3a3a, 0x91919191, 0x11111111, 0x41414141, + 0x4f4f4f4f, 0x67676767, 0xdcdcdcdc, 0xeaeaeaea, + 0x97979797, 0xf2f2f2f2, 0xcfcfcfcf, 0xcececece, + 0xf0f0f0f0, 0xb4b4b4b4, 0xe6e6e6e6, 0x73737373, + 0x96969696, 0xacacacac, 0x74747474, 0x22222222, + 0xe7e7e7e7, 0xadadadad, 0x35353535, 0x85858585, + 0xe2e2e2e2, 0xf9f9f9f9, 0x37373737, 0xe8e8e8e8, + 0x1c1c1c1c, 0x75757575, 0xdfdfdfdf, 0x6e6e6e6e, + 0x47474747, 0xf1f1f1f1, 0x1a1a1a1a, 0x71717171, + 0x1d1d1d1d, 0x29292929, 0xc5c5c5c5, 0x89898989, + 0x6f6f6f6f, 0xb7b7b7b7, 0x62626262, 0x0e0e0e0e, + 0xaaaaaaaa, 0x18181818, 0xbebebebe, 0x1b1b1b1b, + 0xfcfcfcfc, 0x56565656, 0x3e3e3e3e, 0x4b4b4b4b, + 0xc6c6c6c6, 0xd2d2d2d2, 0x79797979, 0x20202020, + 0x9a9a9a9a, 0xdbdbdbdb, 0xc0c0c0c0, 0xfefefefe, + 0x78787878, 0xcdcdcdcd, 0x5a5a5a5a, 0xf4f4f4f4, + 0x1f1f1f1f, 0xdddddddd, 0xa8a8a8a8, 0x33333333, + 0x88888888, 0x07070707, 0xc7c7c7c7, 0x31313131, + 0xb1b1b1b1, 0x12121212, 0x10101010, 0x59595959, + 0x27272727, 0x80808080, 0xecececec, 0x5f5f5f5f, + 0x60606060, 0x51515151, 0x7f7f7f7f, 0xa9a9a9a9, + 0x19191919, 0xb5b5b5b5, 0x4a4a4a4a, 0x0d0d0d0d, + 0x2d2d2d2d, 0xe5e5e5e5, 0x7a7a7a7a, 0x9f9f9f9f, + 0x93939393, 0xc9c9c9c9, 0x9c9c9c9c, 0xefefefef, + 0xa0a0a0a0, 0xe0e0e0e0, 0x3b3b3b3b, 0x4d4d4d4d, + 0xaeaeaeae, 0x2a2a2a2a, 0xf5f5f5f5, 0xb0b0b0b0, + 0xc8c8c8c8, 0xebebebeb, 0xbbbbbbbb, 0x3c3c3c3c, + 0x83838383, 0x53535353, 0x99999999, 0x61616161, + 0x17171717, 0x2b2b2b2b, 0x04040404, 0x7e7e7e7e, + 0xbabababa, 0x77777777, 0xd6d6d6d6, 0x26262626, + 0xe1e1e1e1, 0x69696969, 0x14141414, 0x63636363, + 0x55555555, 0x21212121, 0x0c0c0c0c, 0x7d7d7d7d, +}; +static const uint32_t rcon[] = { + 0x01000000, 0x02000000, 0x04000000, 0x08000000, + 0x10000000, 0x20000000, 0x40000000, 0x80000000, + 0x1B000000, 0x36000000 + /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */ +}; + +#define GETU32(pt) (((uint32_t)((pt)[0] & 0xFF) << 24) ^ \ + ((uint32_t)((pt)[1] & 0xFF) << 16) ^ \ + ((uint32_t)((pt)[2] & 0xFF) << 8) ^ \ + ((uint32_t)((pt)[3] & 0xFF))) +#define PUTU32(ct, st) { \ + (ct)[0] = (char)((st) >> 24); \ + (ct)[1] = (char)((st) >> 16); \ + (ct)[2] = (char)((st) >> 8); \ + (ct)[3] = (char)(st); } + +/** + * Expand the cipher key into the encryption key schedule. + * + * @return the number of rounds for the given cipher key size. + */ +int +rijndaelKeySetupEnc (uint32_t rk[ /*4*(Nr + 1) */ ], + const char cipherKey[], size_t keyBits) +{ + size_t i = 0; + uint32_t temp; + + rk[0] = GETU32 (cipherKey); + rk[1] = GETU32 (cipherKey + 4); + rk[2] = GETU32 (cipherKey + 8); + rk[3] = GETU32 (cipherKey + 12); + if (keyBits == 128) + { + for (;;) + { + temp = rk[3]; + rk[4] = rk[0] ^ + (Te4[(temp >> 16) & 0xff] & 0xff000000) ^ + (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^ + (Te4[(temp) & 0xff] & 0x0000ff00) ^ + (Te4[(temp >> 24)] & 0x000000ff) ^ rcon[i]; + rk[5] = rk[1] ^ rk[4]; + rk[6] = rk[2] ^ rk[5]; + rk[7] = rk[3] ^ rk[6]; + if (++i == 10) + { + return 10; + } + rk += 4; + } + } + rk[4] = GETU32 (cipherKey + 16); + rk[5] = GETU32 (cipherKey + 20); + if (keyBits == 192) + { + for (;;) + { + temp = rk[5]; + rk[6] = rk[0] ^ + (Te4[(temp >> 16) & 0xff] & 0xff000000) ^ + (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^ + (Te4[(temp) & 0xff] & 0x0000ff00) ^ + (Te4[(temp >> 24)] & 0x000000ff) ^ rcon[i]; + rk[7] = rk[1] ^ rk[6]; + rk[8] = rk[2] ^ rk[7]; + rk[9] = rk[3] ^ rk[8]; + if (++i == 8) + { + return 12; + } + rk[10] = rk[4] ^ rk[9]; + rk[11] = rk[5] ^ rk[10]; + rk += 6; + } + } + rk[6] = GETU32 (cipherKey + 24); + rk[7] = GETU32 (cipherKey + 28); + if (keyBits == 256) + { + for (;;) + { + temp = rk[7]; + rk[8] = rk[0] ^ + (Te4[(temp >> 16) & 0xff] & 0xff000000) ^ + (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^ + (Te4[(temp) & 0xff] & 0x0000ff00) ^ + (Te4[(temp >> 24)] & 0x000000ff) ^ rcon[i]; + rk[9] = rk[1] ^ rk[8]; + rk[10] = rk[2] ^ rk[9]; + rk[11] = rk[3] ^ rk[10]; + if (++i == 7) + { + return 14; + } + temp = rk[11]; + rk[12] = rk[4] ^ + (Te4[(temp >> 24)] & 0xff000000) ^ + (Te4[(temp >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(temp >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(temp) & 0xff] & 0x000000ff); + rk[13] = rk[5] ^ rk[12]; + rk[14] = rk[6] ^ rk[13]; + rk[15] = rk[7] ^ rk[14]; + + rk += 8; + } + } + return 0; +} + +/** + * Expand the cipher key into the decryption key schedule. + * + * @return the number of rounds for the given cipher key size. + */ +int +rijndaelKeySetupDec (uint32_t rk[ /*4*(Nr + 1) */ ], + const char cipherKey[], size_t keyBits) +{ + size_t Nr, i, j; + uint32_t temp; + + /* expand the cipher key: */ + Nr = rijndaelKeySetupEnc (rk, cipherKey, keyBits); + /* invert the order of the round keys: */ + for (i = 0, j = 4 * Nr; i < j; i += 4, j -= 4) + { + temp = rk[i]; + rk[i] = rk[j]; + rk[j] = temp; + temp = rk[i + 1]; + rk[i + 1] = rk[j + 1]; + rk[j + 1] = temp; + temp = rk[i + 2]; + rk[i + 2] = rk[j + 2]; + rk[j + 2] = temp; + temp = rk[i + 3]; + rk[i + 3] = rk[j + 3]; + rk[j + 3] = temp; + } + /* apply the inverse MixColumn transform to all round keys but the + first and the last: */ + for (i = 1; i < Nr; i++) + { + rk += 4; + rk[0] = + Td0[Te4[(rk[0] >> 24)] & 0xff] ^ + Td1[Te4[(rk[0] >> 16) & 0xff] & 0xff] ^ + Td2[Te4[(rk[0] >> 8) & 0xff] & 0xff] ^ + Td3[Te4[(rk[0]) & 0xff] & 0xff]; + rk[1] = + Td0[Te4[(rk[1] >> 24)] & 0xff] ^ + Td1[Te4[(rk[1] >> 16) & 0xff] & 0xff] ^ + Td2[Te4[(rk[1] >> 8) & 0xff] & 0xff] ^ + Td3[Te4[(rk[1]) & 0xff] & 0xff]; + rk[2] = + Td0[Te4[(rk[2] >> 24)] & 0xff] ^ + Td1[Te4[(rk[2] >> 16) & 0xff] & 0xff] ^ + Td2[Te4[(rk[2] >> 8) & 0xff] & 0xff] ^ + Td3[Te4[(rk[2]) & 0xff] & 0xff]; + rk[3] = + Td0[Te4[(rk[3] >> 24)] & 0xff] ^ + Td1[Te4[(rk[3] >> 16) & 0xff] & 0xff] ^ + Td2[Te4[(rk[3] >> 8) & 0xff] & 0xff] ^ + Td3[Te4[(rk[3]) & 0xff] & 0xff]; + } + return Nr; +} + +void +rijndaelEncrypt (const uint32_t rk[ /*4*(Nr + 1) */ ], size_t Nr, + const char pt[16], char ct[16]) +{ + uint32_t s0, s1, s2, s3, t0, t1, t2, t3; + size_t r; + + /* + * map byte array block to cipher state + * and add initial round key: + */ + s0 = GETU32 (pt) ^ rk[0]; + s1 = GETU32 (pt + 4) ^ rk[1]; + s2 = GETU32 (pt + 8) ^ rk[2]; + s3 = GETU32 (pt + 12) ^ rk[3]; + /* + * Nr - 1 full rounds: + */ + r = Nr >> 1; + for (;;) + { + t0 = + Te0[(s0 >> 24)] ^ + Te1[(s1 >> 16) & 0xff] ^ + Te2[(s2 >> 8) & 0xff] ^ Te3[(s3) & 0xff] ^ rk[4]; + t1 = + Te0[(s1 >> 24)] ^ + Te1[(s2 >> 16) & 0xff] ^ + Te2[(s3 >> 8) & 0xff] ^ Te3[(s0) & 0xff] ^ rk[5]; + t2 = + Te0[(s2 >> 24)] ^ + Te1[(s3 >> 16) & 0xff] ^ + Te2[(s0 >> 8) & 0xff] ^ Te3[(s1) & 0xff] ^ rk[6]; + t3 = + Te0[(s3 >> 24)] ^ + Te1[(s0 >> 16) & 0xff] ^ + Te2[(s1 >> 8) & 0xff] ^ Te3[(s2) & 0xff] ^ rk[7]; + + rk += 8; + if (--r == 0) + { + break; + } + + s0 = + Te0[(t0 >> 24)] ^ + Te1[(t1 >> 16) & 0xff] ^ + Te2[(t2 >> 8) & 0xff] ^ Te3[(t3) & 0xff] ^ rk[0]; + s1 = + Te0[(t1 >> 24)] ^ + Te1[(t2 >> 16) & 0xff] ^ + Te2[(t3 >> 8) & 0xff] ^ Te3[(t0) & 0xff] ^ rk[1]; + s2 = + Te0[(t2 >> 24)] ^ + Te1[(t3 >> 16) & 0xff] ^ + Te2[(t0 >> 8) & 0xff] ^ Te3[(t1) & 0xff] ^ rk[2]; + s3 = + Te0[(t3 >> 24)] ^ + Te1[(t0 >> 16) & 0xff] ^ + Te2[(t1 >> 8) & 0xff] ^ Te3[(t2) & 0xff] ^ rk[3]; + } + /* + * apply last round and + * map cipher state to byte array block: + */ + s0 = + (Te4[(t0 >> 24)] & 0xff000000) ^ + (Te4[(t1 >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(t2 >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(t3) & 0xff] & 0x000000ff) ^ rk[0]; + PUTU32 (ct, s0); + s1 = + (Te4[(t1 >> 24)] & 0xff000000) ^ + (Te4[(t2 >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(t3 >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(t0) & 0xff] & 0x000000ff) ^ rk[1]; + PUTU32 (ct + 4, s1); + s2 = + (Te4[(t2 >> 24)] & 0xff000000) ^ + (Te4[(t3 >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(t0 >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(t1) & 0xff] & 0x000000ff) ^ rk[2]; + PUTU32 (ct + 8, s2); + s3 = + (Te4[(t3 >> 24)] & 0xff000000) ^ + (Te4[(t0 >> 16) & 0xff] & 0x00ff0000) ^ + (Te4[(t1 >> 8) & 0xff] & 0x0000ff00) ^ + (Te4[(t2) & 0xff] & 0x000000ff) ^ rk[3]; + PUTU32 (ct + 12, s3); +} + +void +rijndaelDecrypt (const uint32_t rk[ /*4*(Nr + 1) */ ], size_t Nr, + const char ct[16], char pt[16]) +{ + uint32_t s0, s1, s2, s3, t0, t1, t2, t3; + size_t r; + + /* + * map byte array block to cipher state + * and add initial round key: + */ + s0 = GETU32 (ct) ^ rk[0]; + s1 = GETU32 (ct + 4) ^ rk[1]; + s2 = GETU32 (ct + 8) ^ rk[2]; + s3 = GETU32 (ct + 12) ^ rk[3]; + /* + * Nr - 1 full rounds: + */ + r = Nr >> 1; + for (;;) + { + t0 = + Td0[(s0 >> 24)] ^ + Td1[(s3 >> 16) & 0xff] ^ + Td2[(s2 >> 8) & 0xff] ^ Td3[(s1) & 0xff] ^ rk[4]; + t1 = + Td0[(s1 >> 24)] ^ + Td1[(s0 >> 16) & 0xff] ^ + Td2[(s3 >> 8) & 0xff] ^ Td3[(s2) & 0xff] ^ rk[5]; + t2 = + Td0[(s2 >> 24)] ^ + Td1[(s1 >> 16) & 0xff] ^ + Td2[(s0 >> 8) & 0xff] ^ Td3[(s3) & 0xff] ^ rk[6]; + t3 = + Td0[(s3 >> 24)] ^ + Td1[(s2 >> 16) & 0xff] ^ + Td2[(s1 >> 8) & 0xff] ^ Td3[(s0) & 0xff] ^ rk[7]; + + rk += 8; + if (--r == 0) + { + break; + } + + s0 = + Td0[(t0 >> 24)] ^ + Td1[(t3 >> 16) & 0xff] ^ + Td2[(t2 >> 8) & 0xff] ^ Td3[(t1) & 0xff] ^ rk[0]; + s1 = + Td0[(t1 >> 24)] ^ + Td1[(t0 >> 16) & 0xff] ^ + Td2[(t3 >> 8) & 0xff] ^ Td3[(t2) & 0xff] ^ rk[1]; + s2 = + Td0[(t2 >> 24)] ^ + Td1[(t1 >> 16) & 0xff] ^ + Td2[(t0 >> 8) & 0xff] ^ Td3[(t3) & 0xff] ^ rk[2]; + s3 = + Td0[(t3 >> 24)] ^ + Td1[(t2 >> 16) & 0xff] ^ + Td2[(t1 >> 8) & 0xff] ^ Td3[(t0) & 0xff] ^ rk[3]; + } + /* + * apply last round and + * map cipher state to byte array block: + */ + s0 = + (Td4[(t0 >> 24)] & 0xff000000) ^ + (Td4[(t3 >> 16) & 0xff] & 0x00ff0000) ^ + (Td4[(t2 >> 8) & 0xff] & 0x0000ff00) ^ + (Td4[(t1) & 0xff] & 0x000000ff) ^ rk[0]; + PUTU32 (pt, s0); + s1 = + (Td4[(t1 >> 24)] & 0xff000000) ^ + (Td4[(t0 >> 16) & 0xff] & 0x00ff0000) ^ + (Td4[(t3 >> 8) & 0xff] & 0x0000ff00) ^ + (Td4[(t2) & 0xff] & 0x000000ff) ^ rk[1]; + PUTU32 (pt + 4, s1); + s2 = + (Td4[(t2 >> 24)] & 0xff000000) ^ + (Td4[(t1 >> 16) & 0xff] & 0x00ff0000) ^ + (Td4[(t0 >> 8) & 0xff] & 0x0000ff00) ^ + (Td4[(t3) & 0xff] & 0x000000ff) ^ rk[2]; + PUTU32 (pt + 8, s2); + s3 = + (Td4[(t3 >> 24)] & 0xff000000) ^ + (Td4[(t2 >> 16) & 0xff] & 0x00ff0000) ^ + (Td4[(t1 >> 8) & 0xff] & 0x0000ff00) ^ + (Td4[(t0) & 0xff] & 0x000000ff) ^ rk[3]; + PUTU32 (pt + 12, s3); +} diff --git a/src/daemon/https/lgl/rijndael-alg-fst.h b/src/daemon/https/lgl/rijndael-alg-fst.h new file mode 100644 index 00000000..88391023 --- /dev/null +++ b/src/daemon/https/lgl/rijndael-alg-fst.h @@ -0,0 +1,67 @@ +/* rijndael-alg-fst.h --- Rijndael cipher implementation. + * Copyright (C) 2005 Free Software Foundation, Inc. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2.1, or (at your + * option) any later version. + * + * This file 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 Lesser General Public License + * along with this file; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +/* Adapted for gnulib by Simon Josefsson. */ + +/** + * rijndael-alg-fst.h + * + * @version 3.0 (December 2000) + * + * Optimised ANSI C code for the Rijndael cipher (now AES) + * + * @author Vincent Rijmen + * @author Antoon Bosselaers + * @author Paulo Barreto + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __RIJNDAEL_ALG_FST_H +#define __RIJNDAEL_ALG_FST_H + +#include +#include + +#define RIJNDAEL_MAXKC (256/32) +#define RIJNDAEL_MAXKB (256/8) +#define RIJNDAEL_MAXNR 14 + +int rijndaelKeySetupEnc (uint32_t rk[ /*4*(Nr + 1) */ ], + const char cipherKey[], size_t keyBits); +int rijndaelKeySetupDec (uint32_t rk[ /*4*(Nr + 1) */ ], + const char cipherKey[], size_t keyBits); +void rijndaelEncrypt (const uint32_t rk[ /*4*(Nr + 1) */ ], size_t Nr, + const char pt[16], char ct[16]); +void rijndaelDecrypt (const uint32_t rk[ /*4*(Nr + 1) */ ], size_t Nr, + const char ct[16], char pt[16]); + +#endif /* __RIJNDAEL_ALG_FST_H */ diff --git a/src/daemon/https/lgl/rijndael-api-fst.c b/src/daemon/https/lgl/rijndael-api-fst.c new file mode 100644 index 00000000..35d920a9 --- /dev/null +++ b/src/daemon/https/lgl/rijndael-api-fst.c @@ -0,0 +1,517 @@ +/* rijndael-api-fst.c --- Rijndael cipher implementation. + * Copyright (C) 2005, 2006 Free Software Foundation, Inc. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2.1, or (at your + * option) any later version. + * + * This file 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 Lesser General Public License + * along with this file; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +/* Adapted for gnulib by Simon Josefsson. + * + * Based on public domain "Optimised C code" retrieved from (SHA1 + * 7c8e4b00d06685d1dbc6724a9e0d502353de339e): + * http://www.iaik.tu-graz.ac.at/research/krypto/AES/old/~rijmen/rijndael/rijndael-fst-3.0.zip + */ + +#include + +/** + * rijndael-api-fst.c + * + * @version 2.9 (December 2000) + * + * Optimised ANSI C code for the Rijndael cipher (now AES) + * + * @author Vincent Rijmen + * @author Antoon Bosselaers + * @author Paulo Barreto + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Acknowledgements: + * + * We are deeply indebted to the following people for their bug reports, + * fixes, and improvement suggestions to this implementation. Though we + * tried to list all contributions, we apologise in advance for any + * missing reference. + * + * Andrew Bales + * Markus Friedl + * John Skodon + */ + +#include "rijndael-alg-fst.h" +#include "rijndael-api-fst.h" + +#include +#include +#include + +rijndael_rc +rijndaelMakeKey (rijndaelKeyInstance * key, rijndael_direction direction, + size_t keyLen, const char *keyMaterial) +{ + size_t i; + char *keyMat; + char cipherKey[RIJNDAEL_MAXKB]; + + if (key == NULL) + { + return RIJNDAEL_BAD_KEY_INSTANCE; + } + + if ((direction == RIJNDAEL_DIR_ENCRYPT) + || (direction == RIJNDAEL_DIR_DECRYPT)) + { + key->direction = direction; + } + else + { + return RIJNDAEL_BAD_KEY_DIR; + } + + if ((keyLen == 128) || (keyLen == 192) || (keyLen == 256)) + { + key->keyLen = keyLen; + } + else + { + return RIJNDAEL_BAD_KEY_MAT; + } + + if (keyMaterial != NULL) + { + strncpy (key->keyMaterial, keyMaterial, keyLen / 4); + } + + /* initialize key schedule: */ + keyMat = key->keyMaterial; + for (i = 0; i < key->keyLen / 8; i++) + { + char t, v; + + t = *keyMat++; + if ((t >= '0') && (t <= '9')) + v = (t - '0') << 4; + else if ((t >= 'a') && (t <= 'f')) + v = (t - 'a' + 10) << 4; + else if ((t >= 'A') && (t <= 'F')) + v = (t - 'A' + 10) << 4; + else + return RIJNDAEL_BAD_KEY_MAT; + + t = *keyMat++; + if ((t >= '0') && (t <= '9')) + v ^= (t - '0'); + else if ((t >= 'a') && (t <= 'f')) + v ^= (t - 'a' + 10); + else if ((t >= 'A') && (t <= 'F')) + v ^= (t - 'A' + 10); + else + return RIJNDAEL_BAD_KEY_MAT; + + cipherKey[i] = v; + } + if (direction == RIJNDAEL_DIR_ENCRYPT) + { + key->Nr = rijndaelKeySetupEnc (key->rk, cipherKey, keyLen); + } + else + { + key->Nr = rijndaelKeySetupDec (key->rk, cipherKey, keyLen); + } + rijndaelKeySetupEnc (key->ek, cipherKey, keyLen); + return 0; +} + +rijndael_rc +rijndaelCipherInit (rijndaelCipherInstance * cipher, rijndael_mode mode, + const char *IV) +{ + if ((mode == RIJNDAEL_MODE_ECB) || (mode == RIJNDAEL_MODE_CBC) + || (mode == RIJNDAEL_MODE_CFB1)) + { + cipher->mode = mode; + } + else + { + return RIJNDAEL_BAD_CIPHER_MODE; + } + if (IV != NULL) + { + int i; + for (i = 0; i < RIJNDAEL_MAX_IV_SIZE; i++) + { + int t, j; + + t = IV[2 * i]; + if ((t >= '0') && (t <= '9')) + j = (t - '0') << 4; + else if ((t >= 'a') && (t <= 'f')) + j = (t - 'a' + 10) << 4; + else if ((t >= 'A') && (t <= 'F')) + j = (t - 'A' + 10) << 4; + else + return RIJNDAEL_BAD_CIPHER_INSTANCE; + + t = IV[2 * i + 1]; + if ((t >= '0') && (t <= '9')) + j ^= (t - '0'); + else if ((t >= 'a') && (t <= 'f')) + j ^= (t - 'a' + 10); + else if ((t >= 'A') && (t <= 'F')) + j ^= (t - 'A' + 10); + else + return RIJNDAEL_BAD_CIPHER_INSTANCE; + + cipher->IV[i] = (uint8_t) j; + } + } + else + { + memset (cipher->IV, 0, RIJNDAEL_MAX_IV_SIZE); + } + return 0; +} + +int +rijndaelBlockEncrypt (rijndaelCipherInstance * cipher, + const rijndaelKeyInstance * key, + const char *input, size_t inputLen, char *outBuffer) +{ + size_t i, k, t, numBlocks; + char block[16], *iv; + + if (cipher == NULL || key == NULL || key->direction == RIJNDAEL_DIR_DECRYPT) + { + return RIJNDAEL_BAD_CIPHER_STATE; + } + if (input == NULL || inputLen <= 0) + { + return 0; /* nothing to do */ + } + + numBlocks = inputLen / 128; + + switch (cipher->mode) + { + case RIJNDAEL_MODE_ECB: + for (i = numBlocks; i > 0; i--) + { + rijndaelEncrypt (key->rk, key->Nr, input, outBuffer); + input += 16; + outBuffer += 16; + } + break; + + case RIJNDAEL_MODE_CBC: + iv = cipher->IV; + for (i = numBlocks; i > 0; i--) + { + ((uint32_t *) block)[0] = ((uint32_t *) input)[0] ^ + ((uint32_t *) iv)[0]; + ((uint32_t *) block)[1] = ((uint32_t *) input)[1] ^ + ((uint32_t *) iv)[1]; + ((uint32_t *) block)[2] = ((uint32_t *) input)[2] ^ + ((uint32_t *) iv)[2]; + ((uint32_t *) block)[3] = ((uint32_t *) input)[3] ^ + ((uint32_t *) iv)[3]; + rijndaelEncrypt (key->rk, key->Nr, block, outBuffer); + memcpy (cipher->IV, outBuffer, 16); + input += 16; + outBuffer += 16; + } + break; + + case RIJNDAEL_MODE_CFB1: + iv = cipher->IV; + for (i = numBlocks; i > 0; i--) + { + memcpy (outBuffer, input, 16); + for (k = 0; k < 128; k++) + { + rijndaelEncrypt (key->ek, key->Nr, iv, block); + outBuffer[k >> 3] ^= (block[0] & 0x80U) >> (k & 7); + for (t = 0; t < 15; t++) + { + iv[t] = (iv[t] << 1) | (iv[t + 1] >> 7); + } + iv[15] = (iv[15] << 1) | + ((outBuffer[k >> 3] >> (7 - (k & 7))) & 1); + } + outBuffer += 16; + input += 16; + } + break; + + default: + return RIJNDAEL_BAD_CIPHER_STATE; + } + + return 128 * numBlocks; +} + +int +rijndaelPadEncrypt (rijndaelCipherInstance * cipher, + const rijndaelKeyInstance * key, + const char *input, size_t inputOctets, char *outBuffer) +{ + size_t i, numBlocks, padLen; + char block[16], *iv; + + if (cipher == NULL || key == NULL || key->direction == RIJNDAEL_DIR_DECRYPT) + { + return RIJNDAEL_BAD_CIPHER_STATE; + } + if (input == NULL || inputOctets <= 0) + { + return 0; /* nothing to do */ + } + + numBlocks = inputOctets / 16; + + switch (cipher->mode) + { + case RIJNDAEL_MODE_ECB: + for (i = numBlocks; i > 0; i--) + { + rijndaelEncrypt (key->rk, key->Nr, input, outBuffer); + input += 16; + outBuffer += 16; + } + padLen = 16 - (inputOctets - 16 * numBlocks); + assert (padLen > 0 && padLen <= 16); + memcpy (block, input, 16 - padLen); + memset (block + 16 - padLen, padLen, padLen); + rijndaelEncrypt (key->rk, key->Nr, block, outBuffer); + break; + + case RIJNDAEL_MODE_CBC: + iv = cipher->IV; + for (i = numBlocks; i > 0; i--) + { + ((uint32_t *) block)[0] = ((uint32_t *) input)[0] ^ + ((uint32_t *) iv)[0]; + ((uint32_t *) block)[1] = ((uint32_t *) input)[1] ^ + ((uint32_t *) iv)[1]; + ((uint32_t *) block)[2] = ((uint32_t *) input)[2] ^ + ((uint32_t *) iv)[2]; + ((uint32_t *) block)[3] = ((uint32_t *) input)[3] ^ + ((uint32_t *) iv)[3]; + rijndaelEncrypt (key->rk, key->Nr, block, outBuffer); + memcpy (cipher->IV, outBuffer, 16); + input += 16; + outBuffer += 16; + } + padLen = 16 - (inputOctets - 16 * numBlocks); + assert (padLen > 0 && padLen <= 16); + for (i = 0; i < 16 - padLen; i++) + { + block[i] = input[i] ^ iv[i]; + } + for (i = 16 - padLen; i < 16; i++) + { + block[i] = (char) padLen ^ iv[i]; + } + rijndaelEncrypt (key->rk, key->Nr, block, outBuffer); + memcpy (cipher->IV, outBuffer, 16); + break; + + default: + return RIJNDAEL_BAD_CIPHER_STATE; + } + + return 16 * (numBlocks + 1); +} + +int +rijndaelBlockDecrypt (rijndaelCipherInstance * cipher, + const rijndaelKeyInstance * key, + const char *input, size_t inputLen, char *outBuffer) +{ + size_t i, k, t, numBlocks; + char block[16], *iv; + + if (cipher == NULL + || key == NULL + || (cipher->mode != RIJNDAEL_MODE_CFB1 + && key->direction == RIJNDAEL_DIR_ENCRYPT)) + { + return RIJNDAEL_BAD_CIPHER_STATE; + } + if (input == NULL || inputLen <= 0) + { + return 0; /* nothing to do */ + } + + numBlocks = inputLen / 128; + + switch (cipher->mode) + { + case RIJNDAEL_MODE_ECB: + for (i = numBlocks; i > 0; i--) + { + rijndaelDecrypt (key->rk, key->Nr, input, outBuffer); + input += 16; + outBuffer += 16; + } + break; + + case RIJNDAEL_MODE_CBC: + iv = cipher->IV; + for (i = numBlocks; i > 0; i--) + { + rijndaelDecrypt (key->rk, key->Nr, input, block); + ((uint32_t *) block)[0] ^= ((uint32_t *) iv)[0]; + ((uint32_t *) block)[1] ^= ((uint32_t *) iv)[1]; + ((uint32_t *) block)[2] ^= ((uint32_t *) iv)[2]; + ((uint32_t *) block)[3] ^= ((uint32_t *) iv)[3]; + memcpy (cipher->IV, input, 16); + memcpy (outBuffer, block, 16); + input += 16; + outBuffer += 16; + } + break; + + case RIJNDAEL_MODE_CFB1: + iv = cipher->IV; + for (i = numBlocks; i > 0; i--) + { + memcpy (outBuffer, input, 16); + for (k = 0; k < 128; k++) + { + rijndaelEncrypt (key->ek, key->Nr, iv, block); + for (t = 0; t < 15; t++) + { + iv[t] = (iv[t] << 1) | (iv[t + 1] >> 7); + } + iv[15] = (iv[15] << 1) | ((input[k >> 3] >> (7 - (k & 7))) & 1); + outBuffer[k >> 3] ^= (block[0] & 0x80U) >> (k & 7); + } + outBuffer += 16; + input += 16; + } + break; + + default: + return RIJNDAEL_BAD_CIPHER_STATE; + } + + return 128 * numBlocks; +} + +int +rijndaelPadDecrypt (rijndaelCipherInstance * cipher, + const rijndaelKeyInstance * key, + const char *input, size_t inputOctets, char *outBuffer) +{ + size_t i, numBlocks, padLen; + char block[16]; + + if (cipher == NULL || key == NULL || key->direction == RIJNDAEL_DIR_ENCRYPT) + { + return RIJNDAEL_BAD_CIPHER_STATE; + } + if (input == NULL || inputOctets <= 0) + { + return 0; /* nothing to do */ + } + if (inputOctets % 16 != 0) + { + return RIJNDAEL_BAD_DATA; + } + + numBlocks = inputOctets / 16; + + switch (cipher->mode) + { + case RIJNDAEL_MODE_ECB: + /* all blocks but last */ + for (i = numBlocks - 1; i > 0; i--) + { + rijndaelDecrypt (key->rk, key->Nr, input, outBuffer); + input += 16; + outBuffer += 16; + } + /* last block */ + rijndaelDecrypt (key->rk, key->Nr, input, block); + padLen = block[15]; + if (padLen >= 16) + { + return RIJNDAEL_BAD_DATA; + } + for (i = 16 - padLen; i < 16; i++) + { + if (block[i] != padLen) + { + return RIJNDAEL_BAD_DATA; + } + } + memcpy (outBuffer, block, 16 - padLen); + break; + + case RIJNDAEL_MODE_CBC: + /* all blocks but last */ + for (i = numBlocks - 1; i > 0; i--) + { + rijndaelDecrypt (key->rk, key->Nr, input, block); + ((uint32_t *) block)[0] ^= ((uint32_t *) cipher->IV)[0]; + ((uint32_t *) block)[1] ^= ((uint32_t *) cipher->IV)[1]; + ((uint32_t *) block)[2] ^= ((uint32_t *) cipher->IV)[2]; + ((uint32_t *) block)[3] ^= ((uint32_t *) cipher->IV)[3]; + memcpy (cipher->IV, input, 16); + memcpy (outBuffer, block, 16); + input += 16; + outBuffer += 16; + } + /* last block */ + rijndaelDecrypt (key->rk, key->Nr, input, block); + ((uint32_t *) block)[0] ^= ((uint32_t *) cipher->IV)[0]; + ((uint32_t *) block)[1] ^= ((uint32_t *) cipher->IV)[1]; + ((uint32_t *) block)[2] ^= ((uint32_t *) cipher->IV)[2]; + ((uint32_t *) block)[3] ^= ((uint32_t *) cipher->IV)[3]; + padLen = block[15]; + if (padLen <= 0 || padLen > 16) + { + return RIJNDAEL_BAD_DATA; + } + for (i = 16 - padLen; i < 16; i++) + { + if (block[i] != padLen) + { + return RIJNDAEL_BAD_DATA; + } + } + memcpy (outBuffer, block, 16 - padLen); + break; + + default: + return RIJNDAEL_BAD_CIPHER_STATE; + } + + return 16 * numBlocks - padLen; +} diff --git a/src/daemon/https/lgl/rijndael-api-fst.h b/src/daemon/https/lgl/rijndael-api-fst.h new file mode 100644 index 00000000..d0ff60ac --- /dev/null +++ b/src/daemon/https/lgl/rijndael-api-fst.h @@ -0,0 +1,207 @@ +/* rijndael-api-fst.h --- Rijndael cipher implementation. + * Copyright (C) 2005 Free Software Foundation, Inc. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2.1, or (at your + * option) any later version. + * + * This file 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 Lesser General Public License + * along with this file; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +/* Adapted for gnulib by Simon Josefsson. */ + +/** + * rijndael-api-fst.h + * + * @version 2.9 (December 2000) + * + * Optimised ANSI C code for the Rijndael cipher (now AES) + * + * @author Vincent Rijmen + * @author Antoon Bosselaers + * @author Paulo Barreto + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Acknowledgements: + * + * We are deeply indebted to the following people for their bug reports, + * fixes, and improvement suggestions to this implementation. Though we + * tried to list all contributions, we apologise in advance for any + * missing reference. + * + * Andrew Bales + * Markus Friedl + * John Skodon + */ + +#ifndef __RIJNDAEL_API_FST_H +#define __RIJNDAEL_API_FST_H + +#include "rijndael-alg-fst.h" + +#include + +/* Default number of bits in a cipher block */ +#define RIJNDAEL_BITSPERBLOCK 128 + +/* Number of ASCII char's needed to represent a key */ +#define RIJNDAEL_MAX_KEY_SIZE 64 + +/* Number bytes needed to represent an IV */ +#define RIJNDAEL_MAX_IV_SIZE 16 + +typedef enum +{ + /* Key direction is invalid, e.g., unknown value */ + RIJNDAEL_BAD_KEY_DIR = -1, + /* Key material not of correct length */ + RIJNDAEL_BAD_KEY_MAT = -2, + /* Key passed is not valid */ + RIJNDAEL_BAD_KEY_INSTANCE = -3, + /* Params struct passed to cipherInit invalid */ + RIJNDAEL_BAD_CIPHER_MODE = -4, + /* Cipher in wrong state (e.g., not initialized) */ + RIJNDAEL_BAD_CIPHER_STATE = -5, + RIJNDAEL_BAD_BLOCK_LENGTH = -6, + RIJNDAEL_BAD_CIPHER_INSTANCE = -7, + /* Data contents are invalid, e.g., invalid padding */ + RIJNDAEL_BAD_DATA = -8, + /* Unknown error */ + RIJNDAEL_BAD_OTHER = -9 +} rijndael_rc; + +typedef enum +{ + RIJNDAEL_DIR_ENCRYPT = 0, /* Are we encrypting? */ + RIJNDAEL_DIR_DECRYPT = 1 /* Are we decrypting? */ +} rijndael_direction; + +typedef enum +{ + RIJNDAEL_MODE_ECB = 1, /* Are we ciphering in ECB mode? */ + RIJNDAEL_MODE_CBC = 2, /* Are we ciphering in CBC mode? */ + RIJNDAEL_MODE_CFB1 = 3 /* Are we ciphering in 1-bit CFB mode? */ +} rijndael_mode; + +/* The structure for key information */ +typedef struct +{ + /* Key used for encrypting or decrypting? */ + rijndael_direction direction; + /* Length of the key */ + size_t keyLen; + /* Raw key data in ASCII, e.g., user input or KAT values */ + char keyMaterial[RIJNDAEL_MAX_KEY_SIZE + 1]; + /* key-length-dependent number of rounds */ + int Nr; + /* key schedule */ + uint32_t rk[4 * (RIJNDAEL_MAXNR + 1)]; + /* CFB1 key schedule (encryption only) */ + uint32_t ek[4 * (RIJNDAEL_MAXNR + 1)]; +} rijndaelKeyInstance; + +/* The structure for cipher information */ +typedef struct +{ /* changed order of the components */ + rijndael_mode mode; /* MODE_ECB, MODE_CBC, or MODE_CFB1 */ + /* A possible Initialization Vector for ciphering */ + char IV[RIJNDAEL_MAX_IV_SIZE]; +} rijndaelCipherInstance; + +/* Function prototypes */ + +/* Create KEY, for encryption or decryption depending on DIRECTION, + from KEYMATERIAL, a hex string, of KEYLEN size. KEYLEN should be + 128, 192 or 256. Returns 0 on success, or an error code. */ +extern rijndael_rc +rijndaelMakeKey (rijndaelKeyInstance *key, rijndael_direction direction, + size_t keyLen, const char *keyMaterial); + +/* Initialize cipher state CIPHER for encryption MODE (e.g., + RIJNDAEL_MODE_CBC) with initialization vector IV, a hex string of + 2*RIJNDAEL_MAX_IV_SIZE length. IV may be NULL for modes that do + not need an IV (i.e., RIJNDAEL_MODE_ECB). */ +extern rijndael_rc +rijndaelCipherInit (rijndaelCipherInstance *cipher, + rijndael_mode mode, const char *IV); + +/* Encrypt data in INPUT, of INPUTLEN/8 bytes length, placing the + output in the pre-allocated OUTBUFFER which must hold at least + INPUTLEN/8 bytes of data. The CIPHER is used as state, and must be + initialized with rijndaelCipherInit before calling this function. + The encryption KEY must be initialized with rijndaelMakeKey before + calling this function. Return the number of bits written, or a + negative rijndael_rc error code. */ +extern int +rijndaelBlockEncrypt (rijndaelCipherInstance *cipher, + const rijndaelKeyInstance *key, + const char *input, size_t inputLen, + char *outBuffer); + +/* Encrypt data in INPUT, of INPUTOCTETS bytes length, placing the + output in the pre-allocated OUTBUFFER which must hold at least + INPUTOCTETS aligned to the next block size boundary. + Ciphertext-Stealing as described in RFC 2040 is used to encrypt + partial blocks. The CIPHER is used as state, and must be + initialized with rijndaelCipherInit before calling this function. + The encryption KEY must be initialized with rijndaelMakeKey before + calling this function. Return the number of bits written, or a + negative rijndael_rc error code. */ +extern int +rijndaelPadEncrypt (rijndaelCipherInstance *cipher, + const rijndaelKeyInstance *key, + const char *input, size_t inputOctets, + char *outBuffer); + +/* Decrypt data in INPUT, of INPUTLEN/8 bytes length, placing the + output in the pre-allocated OUTBUFFER which must hold at least + INPUTLEN/8 bytes of data. The CIPHER is used as state, and must be + initialized with rijndaelCipherInit before calling this function. + The encryption KEY must be initialized with rijndaelMakeKey before + calling this function. Return the number of bits written, or a + negative rijndael_rc error code. */ +extern int +rijndaelBlockDecrypt (rijndaelCipherInstance *cipher, + const rijndaelKeyInstance *key, + const char *input, size_t inputLen, + char *outBuffer); + +/* Decrypt data in INPUT, of INPUTOCTETS bytes length, placing the + output in the pre-allocated OUTBUFFER which must hold at least + INPUTOCTETS aligned to the next block size boundary. + Ciphertext-Stealing as described in RFC 2040 is used to encrypt + partial blocks. The CIPHER is used as state, and must be + initialized with rijndaelCipherInit before calling this function. + The encryption KEY must be initialized with rijndaelMakeKey before + calling this function. Return the number of bits written, or a + negative rijndael_rc error code. */ +extern int +rijndaelPadDecrypt (rijndaelCipherInstance *cipher, + const rijndaelKeyInstance *key, + const char *input, size_t inputOctets, + char *outBuffer); + +#endif /* __RIJNDAEL_API_FST_H */ diff --git a/src/daemon/https/lgl/sha1.c b/src/daemon/https/lgl/sha1.c new file mode 100644 index 00000000..f5573da5 --- /dev/null +++ b/src/daemon/https/lgl/sha1.c @@ -0,0 +1,416 @@ +/* sha1.c - Functions to compute SHA1 message digest of files or + memory blocks according to the NIST specification FIPS-180-1. + + Copyright (C) 2000, 2001, 2003, 2004, 2005, 2006 Free Software + Foundation, Inc. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by the + Free Software Foundation; either version 2.1, or (at your option) any + later version. + + This program 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Written by Scott G. Miller + Credits: + Robert Klep -- Expansion function fix +*/ + +#include + +#include "sha1.h" + +#include +#include + +#if USE_UNLOCKED_IO +# include "unlocked-io.h" +#endif + +#ifdef WORDS_BIGENDIAN +# define SWAP(n) (n) +#else +# define SWAP(n) \ + (((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24)) +#endif + +#define BLOCKSIZE 4096 +#if BLOCKSIZE % 64 != 0 +# error "invalid BLOCKSIZE" +#endif + +/* This array contains the bytes used to pad the buffer to the next + 64-byte boundary. (RFC 1321, 3.1: Step 1) */ +static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ... */ }; + + +/* Take a pointer to a 160 bit block of data (five 32 bit ints) and + initialize it to the start constants of the SHA1 algorithm. This + must be called before using hash in the call to sha1_hash. */ +void +sha1_init_ctx (struct sha1_ctx *ctx) +{ + ctx->A = 0x67452301; + ctx->B = 0xefcdab89; + ctx->C = 0x98badcfe; + ctx->D = 0x10325476; + ctx->E = 0xc3d2e1f0; + + ctx->total[0] = ctx->total[1] = 0; + ctx->buflen = 0; +} + +/* Put result from CTX in first 20 bytes following RESBUF. The result + must be in little endian byte order. + + IMPORTANT: On some systems it is required that RESBUF is correctly + aligned for a 32-bit value. */ +void * +sha1_read_ctx (const struct sha1_ctx *ctx, void *resbuf) +{ + ((uint32_t *) resbuf)[0] = SWAP (ctx->A); + ((uint32_t *) resbuf)[1] = SWAP (ctx->B); + ((uint32_t *) resbuf)[2] = SWAP (ctx->C); + ((uint32_t *) resbuf)[3] = SWAP (ctx->D); + ((uint32_t *) resbuf)[4] = SWAP (ctx->E); + + return resbuf; +} + +/* Process the remaining bytes in the internal buffer and the usual + prolog according to the standard and write the result to RESBUF. + + IMPORTANT: On some systems it is required that RESBUF is correctly + aligned for a 32-bit value. */ +void * +sha1_finish_ctx (struct sha1_ctx *ctx, void *resbuf) +{ + /* Take yet unprocessed bytes into account. */ + uint32_t bytes = ctx->buflen; + size_t size = (bytes < 56) ? 64 / 4 : 64 * 2 / 4; + + /* Now count remaining bytes. */ + ctx->total[0] += bytes; + if (ctx->total[0] < bytes) + ++ctx->total[1]; + + /* Put the 64-bit file length in *bits* at the end of the buffer. */ + ctx->buffer[size - 2] = SWAP ((ctx->total[1] << 3) | (ctx->total[0] >> 29)); + ctx->buffer[size - 1] = SWAP (ctx->total[0] << 3); + + memcpy (&((char *) ctx->buffer)[bytes], fillbuf, (size - 2) * 4 - bytes); + + /* Process last bytes. */ + sha1_process_block (ctx->buffer, size * 4, ctx); + + return sha1_read_ctx (ctx, resbuf); +} + +/* Compute SHA1 message digest for bytes read from STREAM. The + resulting message digest number will be written into the 16 bytes + beginning at RESBLOCK. */ +int +sha1_stream (FILE * stream, void *resblock) +{ + struct sha1_ctx ctx; + char buffer[BLOCKSIZE + 72]; + size_t sum; + + /* Initialize the computation context. */ + sha1_init_ctx (&ctx); + + /* Iterate over full file contents. */ + while (1) + { + /* We read the file in blocks of BLOCKSIZE bytes. One call of the + computation function processes the whole buffer so that with the + next round of the loop another block can be read. */ + size_t n; + sum = 0; + + /* Read block. Take care for partial reads. */ + while (1) + { + n = fread (buffer + sum, 1, BLOCKSIZE - sum, stream); + + sum += n; + + if (sum == BLOCKSIZE) + break; + + if (n == 0) + { + /* Check for the error flag IFF N == 0, so that we don't + exit the loop after a partial read due to e.g., EAGAIN + or EWOULDBLOCK. */ + if (ferror (stream)) + return 1; + goto process_partial_block; + } + + /* We've read at least one byte, so ignore errors. But always + check for EOF, since feof may be true even though N > 0. + Otherwise, we could end up calling fread after EOF. */ + if (feof (stream)) + goto process_partial_block; + } + + /* Process buffer with BLOCKSIZE bytes. Note that + BLOCKSIZE % 64 == 0 + */ + sha1_process_block (buffer, BLOCKSIZE, &ctx); + } + +process_partial_block:; + + /* Process any remaining bytes. */ + if (sum > 0) + sha1_process_bytes (buffer, sum, &ctx); + + /* Construct result in desired memory. */ + sha1_finish_ctx (&ctx, resblock); + return 0; +} + +/* Compute SHA1 message digest for LEN bytes beginning at BUFFER. The + result is always in little endian byte order, so that a byte-wise + output yields to the wanted ASCII representation of the message + digest. */ +void * +sha1_buffer (const char *buffer, size_t len, void *resblock) +{ + struct sha1_ctx ctx; + + /* Initialize the computation context. */ + sha1_init_ctx (&ctx); + + /* Process whole buffer but last len % 64 bytes. */ + sha1_process_bytes (buffer, len, &ctx); + + /* Put result in desired memory area. */ + return sha1_finish_ctx (&ctx, resblock); +} + +void +sha1_process_bytes (const void *buffer, size_t len, struct sha1_ctx *ctx) +{ + /* When we already have some bits in our internal buffer concatenate + both inputs first. */ + if (ctx->buflen != 0) + { + size_t left_over = ctx->buflen; + size_t add = 128 - left_over > len ? len : 128 - left_over; + + memcpy (&((char *) ctx->buffer)[left_over], buffer, add); + ctx->buflen += add; + + if (ctx->buflen > 64) + { + sha1_process_block (ctx->buffer, ctx->buflen & ~63, ctx); + + ctx->buflen &= 63; + /* The regions in the following copy operation cannot overlap. */ + memcpy (ctx->buffer, + &((char *) ctx->buffer)[(left_over + add) & ~63], + ctx->buflen); + } + + buffer = (const char *) buffer + add; + len -= add; + } + + /* Process available complete blocks. */ + if (len >= 64) + { +#if !_STRING_ARCH_unaligned +# define alignof(type) offsetof (struct { char c; type x; }, x) +# define UNALIGNED_P(p) (((size_t) p) % alignof (uint32_t) != 0) + if (UNALIGNED_P (buffer)) + while (len > 64) + { + sha1_process_block (memcpy (ctx->buffer, buffer, 64), 64, ctx); + buffer = (const char *) buffer + 64; + len -= 64; + } + else +#endif + { + sha1_process_block (buffer, len & ~63, ctx); + buffer = (const char *) buffer + (len & ~63); + len &= 63; + } + } + + /* Move remaining bytes in internal buffer. */ + if (len > 0) + { + size_t left_over = ctx->buflen; + + memcpy (&((char *) ctx->buffer)[left_over], buffer, len); + left_over += len; + if (left_over >= 64) + { + sha1_process_block (ctx->buffer, 64, ctx); + left_over -= 64; + memcpy (ctx->buffer, &ctx->buffer[16], left_over); + } + ctx->buflen = left_over; + } +} + +/* --- Code below is the primary difference between md5.c and sha1.c --- */ + +/* SHA1 round constants */ +#define K1 0x5a827999 +#define K2 0x6ed9eba1 +#define K3 0x8f1bbcdc +#define K4 0xca62c1d6 + +/* Round functions. Note that F2 is the same as F4. */ +#define F1(B,C,D) ( D ^ ( B & ( C ^ D ) ) ) +#define F2(B,C,D) (B ^ C ^ D) +#define F3(B,C,D) ( ( B & C ) | ( D & ( B | C ) ) ) +#define F4(B,C,D) (B ^ C ^ D) + +/* Process LEN bytes of BUFFER, accumulating context into CTX. + It is assumed that LEN % 64 == 0. + Most of this code comes from GnuPG's cipher/sha1.c. */ + +void +sha1_process_block (const void *buffer, size_t len, struct sha1_ctx *ctx) +{ + const uint32_t *words = buffer; + size_t nwords = len / sizeof (uint32_t); + const uint32_t *endp = words + nwords; + uint32_t x[16]; + uint32_t a = ctx->A; + uint32_t b = ctx->B; + uint32_t c = ctx->C; + uint32_t d = ctx->D; + uint32_t e = ctx->E; + + /* First increment the byte count. RFC 1321 specifies the possible + length of the file up to 2^64 bits. Here we only compute the + number of bytes. Do a double word increment. */ + ctx->total[0] += len; + if (ctx->total[0] < len) + ++ctx->total[1]; + +#define rol(x, n) (((x) << (n)) | ((uint32_t) (x) >> (32 - (n)))) + +#define M(I) ( tm = x[I&0x0f] ^ x[(I-14)&0x0f] \ + ^ x[(I-8)&0x0f] ^ x[(I-3)&0x0f] \ + , (x[I&0x0f] = rol(tm, 1)) ) + +#define R(A,B,C,D,E,F,K,M) do { E += rol( A, 5 ) \ + + F( B, C, D ) \ + + K \ + + M; \ + B = rol( B, 30 ); \ + } while(0) + + while (words < endp) + { + uint32_t tm; + int t; + for (t = 0; t < 16; t++) + { + x[t] = SWAP (*words); + words++; + } + + R (a, b, c, d, e, F1, K1, x[0]); + R (e, a, b, c, d, F1, K1, x[1]); + R (d, e, a, b, c, F1, K1, x[2]); + R (c, d, e, a, b, F1, K1, x[3]); + R (b, c, d, e, a, F1, K1, x[4]); + R (a, b, c, d, e, F1, K1, x[5]); + R (e, a, b, c, d, F1, K1, x[6]); + R (d, e, a, b, c, F1, K1, x[7]); + R (c, d, e, a, b, F1, K1, x[8]); + R (b, c, d, e, a, F1, K1, x[9]); + R (a, b, c, d, e, F1, K1, x[10]); + R (e, a, b, c, d, F1, K1, x[11]); + R (d, e, a, b, c, F1, K1, x[12]); + R (c, d, e, a, b, F1, K1, x[13]); + R (b, c, d, e, a, F1, K1, x[14]); + R (a, b, c, d, e, F1, K1, x[15]); + R (e, a, b, c, d, F1, K1, M (16)); + R (d, e, a, b, c, F1, K1, M (17)); + R (c, d, e, a, b, F1, K1, M (18)); + R (b, c, d, e, a, F1, K1, M (19)); + R (a, b, c, d, e, F2, K2, M (20)); + R (e, a, b, c, d, F2, K2, M (21)); + R (d, e, a, b, c, F2, K2, M (22)); + R (c, d, e, a, b, F2, K2, M (23)); + R (b, c, d, e, a, F2, K2, M (24)); + R (a, b, c, d, e, F2, K2, M (25)); + R (e, a, b, c, d, F2, K2, M (26)); + R (d, e, a, b, c, F2, K2, M (27)); + R (c, d, e, a, b, F2, K2, M (28)); + R (b, c, d, e, a, F2, K2, M (29)); + R (a, b, c, d, e, F2, K2, M (30)); + R (e, a, b, c, d, F2, K2, M (31)); + R (d, e, a, b, c, F2, K2, M (32)); + R (c, d, e, a, b, F2, K2, M (33)); + R (b, c, d, e, a, F2, K2, M (34)); + R (a, b, c, d, e, F2, K2, M (35)); + R (e, a, b, c, d, F2, K2, M (36)); + R (d, e, a, b, c, F2, K2, M (37)); + R (c, d, e, a, b, F2, K2, M (38)); + R (b, c, d, e, a, F2, K2, M (39)); + R (a, b, c, d, e, F3, K3, M (40)); + R (e, a, b, c, d, F3, K3, M (41)); + R (d, e, a, b, c, F3, K3, M (42)); + R (c, d, e, a, b, F3, K3, M (43)); + R (b, c, d, e, a, F3, K3, M (44)); + R (a, b, c, d, e, F3, K3, M (45)); + R (e, a, b, c, d, F3, K3, M (46)); + R (d, e, a, b, c, F3, K3, M (47)); + R (c, d, e, a, b, F3, K3, M (48)); + R (b, c, d, e, a, F3, K3, M (49)); + R (a, b, c, d, e, F3, K3, M (50)); + R (e, a, b, c, d, F3, K3, M (51)); + R (d, e, a, b, c, F3, K3, M (52)); + R (c, d, e, a, b, F3, K3, M (53)); + R (b, c, d, e, a, F3, K3, M (54)); + R (a, b, c, d, e, F3, K3, M (55)); + R (e, a, b, c, d, F3, K3, M (56)); + R (d, e, a, b, c, F3, K3, M (57)); + R (c, d, e, a, b, F3, K3, M (58)); + R (b, c, d, e, a, F3, K3, M (59)); + R (a, b, c, d, e, F4, K4, M (60)); + R (e, a, b, c, d, F4, K4, M (61)); + R (d, e, a, b, c, F4, K4, M (62)); + R (c, d, e, a, b, F4, K4, M (63)); + R (b, c, d, e, a, F4, K4, M (64)); + R (a, b, c, d, e, F4, K4, M (65)); + R (e, a, b, c, d, F4, K4, M (66)); + R (d, e, a, b, c, F4, K4, M (67)); + R (c, d, e, a, b, F4, K4, M (68)); + R (b, c, d, e, a, F4, K4, M (69)); + R (a, b, c, d, e, F4, K4, M (70)); + R (e, a, b, c, d, F4, K4, M (71)); + R (d, e, a, b, c, F4, K4, M (72)); + R (c, d, e, a, b, F4, K4, M (73)); + R (b, c, d, e, a, F4, K4, M (74)); + R (a, b, c, d, e, F4, K4, M (75)); + R (e, a, b, c, d, F4, K4, M (76)); + R (d, e, a, b, c, F4, K4, M (77)); + R (c, d, e, a, b, F4, K4, M (78)); + R (b, c, d, e, a, F4, K4, M (79)); + + a = ctx->A += a; + b = ctx->B += b; + c = ctx->C += c; + d = ctx->D += d; + e = ctx->E += e; + } +} diff --git a/src/daemon/https/lgl/sha1.h b/src/daemon/https/lgl/sha1.h new file mode 100644 index 00000000..ed0de2b4 --- /dev/null +++ b/src/daemon/https/lgl/sha1.h @@ -0,0 +1,87 @@ +/* Declarations of functions and data types used for SHA1 sum + library functions. + Copyright (C) 2000, 2001, 2003, 2005, 2006 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by the + Free Software Foundation; either version 2.1, or (at your option) any + later version. + + This program 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef SHA1_H +# define SHA1_H 1 + +# include +# include + +/* Structure to save state of computation between the single steps. */ +struct sha1_ctx +{ + uint32_t A; + uint32_t B; + uint32_t C; + uint32_t D; + uint32_t E; + + uint32_t total[2]; + uint32_t buflen; + uint32_t buffer[32]; +}; + + +/* Initialize structure containing state of computation. */ +extern void sha1_init_ctx (struct sha1_ctx *ctx); + +/* Starting with the result of former calls of this function (or the + initialization function update the context for the next LEN bytes + starting at BUFFER. + It is necessary that LEN is a multiple of 64!!! */ +extern void sha1_process_block (const void *buffer, size_t len, + struct sha1_ctx *ctx); + +/* Starting with the result of former calls of this function (or the + initialization function update the context for the next LEN bytes + starting at BUFFER. + It is NOT required that LEN is a multiple of 64. */ +extern void sha1_process_bytes (const void *buffer, size_t len, + struct sha1_ctx *ctx); + +/* Process the remaining bytes in the buffer and put result from CTX + in first 20 bytes following RESBUF. The result is always in little + endian byte order, so that a byte-wise output yields to the wanted + ASCII representation of the message digest. + + IMPORTANT: On some systems it is required that RESBUF be correctly + aligned for a 32 bits value. */ +extern void *sha1_finish_ctx (struct sha1_ctx *ctx, void *resbuf); + + +/* Put result from CTX in first 20 bytes following RESBUF. The result is + always in little endian byte order, so that a byte-wise output yields + to the wanted ASCII representation of the message digest. + + IMPORTANT: On some systems it is required that RESBUF is correctly + aligned for a 32 bits value. */ +extern void *sha1_read_ctx (const struct sha1_ctx *ctx, void *resbuf); + + +/* Compute SHA1 message digest for bytes read from STREAM. The + resulting message digest number will be written into the 20 bytes + beginning at RESBLOCK. */ +extern int sha1_stream (FILE *stream, void *resblock); + +/* Compute SHA1 message digest for LEN bytes beginning at BUFFER. The + result is always in little endian byte order, so that a byte-wise + output yields to the wanted ASCII representation of the message + digest. */ +extern void *sha1_buffer (const char *buffer, size_t len, void *resblock); + +#endif diff --git a/src/daemon/https/lgl/snprintf.c b/src/daemon/https/lgl/snprintf.c new file mode 100644 index 00000000..538ee08e --- /dev/null +++ b/src/daemon/https/lgl/snprintf.c @@ -0,0 +1,77 @@ +/* Formatted output to strings. + Copyright (C) 2004, 2006-2007 Free Software Foundation, Inc. + Written by Simon Josefsson and Paul Eggert. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1, or (at your option) + any later version. + + This program 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#include + +/* Specification. */ +#include + +#include +#include +#include +#include +#include + +#include "vasnprintf.h" + +/* Some systems, like OSF/1 4.0 and Woe32, don't have EOVERFLOW. */ +#ifndef EOVERFLOW +# define EOVERFLOW E2BIG +#endif + +/* Print formatted output to string STR. Similar to sprintf, but + additional length SIZE limit how much is written into STR. Returns + string length of formatted string (which may be larger than SIZE). + STR may be NULL, in which case nothing will be written. On error, + return a negative value. */ +int +snprintf (char *str, size_t size, const char *format, ...) +{ + char *output; + size_t len; + size_t lenbuf = size; + va_list args; + + va_start (args, format); + output = vasnprintf (str, &lenbuf, format, args); + len = lenbuf; + va_end (args); + + if (!output) + return -1; + + if (output != str) + { + if (size) + { + size_t pruned_len = (len < size ? len : size - 1); + memcpy (str, output, pruned_len); + str[pruned_len] = '\0'; + } + + free (output); + } + + if (INT_MAX < len) + { + errno = EOVERFLOW; + return -1; + } + + return len; +} diff --git a/src/daemon/https/lgl/strverscmp.c b/src/daemon/https/lgl/strverscmp.c new file mode 100644 index 00000000..3b6bd20e --- /dev/null +++ b/src/daemon/https/lgl/strverscmp.c @@ -0,0 +1,130 @@ +/* Compare strings while treating digits characters numerically. + Copyright (C) 1997, 2000, 2002, 2004, 2006 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Jean-François Bignolles , 1997. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1, or (at your option) + any later version. + + This program 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#if !_LIBC +# include +#endif + +#include +#include + +/* states: S_N: normal, S_I: comparing integral part, S_F: comparing + fractional parts, S_Z: idem but with leading Zeroes only */ +#define S_N 0x0 +#define S_I 0x4 +#define S_F 0x8 +#define S_Z 0xC + +/* result_type: CMP: return diff; LEN: compare using len_diff/diff */ +#define CMP 2 +#define LEN 3 + + +/* ISDIGIT differs from isdigit, as follows: + - Its arg may be any int or unsigned int; it need not be an unsigned char + or EOF. + - It's typically faster. + POSIX says that only '0' through '9' are digits. Prefer ISDIGIT to + isdigit unless it's important to use the locale's definition + of `digit' even when the host does not conform to POSIX. */ +#define ISDIGIT(c) ((unsigned int) (c) - '0' <= 9) + +#undef __strverscmp +#undef strverscmp + +#ifndef weak_alias +# define __strverscmp strverscmp +#endif + +/* Compare S1 and S2 as strings holding indices/version numbers, + returning less than, equal to or greater than zero if S1 is less than, + equal to or greater than S2 (for more info, see the texinfo doc). +*/ + +int +__strverscmp (const char *s1, const char *s2) +{ + const unsigned char *p1 = (const unsigned char *) s1; + const unsigned char *p2 = (const unsigned char *) s2; + unsigned char c1, c2; + int state; + int diff; + + /* Symbol(s) 0 [1-9] others (padding) + Transition (10) 0 (01) d (00) x (11) - */ + static const unsigned int next_state[] = { + /* state x d 0 - */ + /* S_N */ S_N, S_I, S_Z, S_N, + /* S_I */ S_N, S_I, S_I, S_I, + /* S_F */ S_N, S_F, S_F, S_F, + /* S_Z */ S_N, S_F, S_Z, S_Z + }; + + static const int result_type[] = { + /* state x/x x/d x/0 x/- d/x d/d d/0 d/- + 0/x 0/d 0/0 0/- -/x -/d -/0 -/- */ + + /* S_N */ CMP, CMP, CMP, CMP, CMP, LEN, CMP, CMP, + CMP, CMP, CMP, CMP, CMP, CMP, CMP, CMP, + /* S_I */ CMP, -1, -1, CMP, 1, LEN, LEN, CMP, + 1, LEN, LEN, CMP, CMP, CMP, CMP, CMP, + /* S_F */ CMP, CMP, CMP, CMP, CMP, LEN, CMP, CMP, + CMP, CMP, CMP, CMP, CMP, CMP, CMP, CMP, + /* S_Z */ CMP, 1, 1, CMP, -1, CMP, CMP, CMP, + -1, CMP, CMP, CMP + }; + + if (p1 == p2) + return 0; + + c1 = *p1++; + c2 = *p2++; + /* Hint: '0' is a digit too. */ + state = S_N | ((c1 == '0') + (ISDIGIT (c1) != 0)); + + while ((diff = c1 - c2) == 0 && c1 != '\0') + { + state = next_state[state]; + c1 = *p1++; + c2 = *p2++; + state |= (c1 == '0') + (ISDIGIT (c1) != 0); + } + + state = result_type[state << 2 | ((c2 == '0') + (ISDIGIT (c2) != 0))]; + + switch (state) + { + case CMP: + return diff; + + case LEN: + while (ISDIGIT (*p1++)) + if (!ISDIGIT (*p2++)) + return 1; + + return ISDIGIT (*p2) ? -1 : diff; + + default: + return state; + } +} + +#ifdef weak_alias +weak_alias (__strverscmp, strverscmp) +#endif diff --git a/src/daemon/https/lgl/strverscmp.h b/src/daemon/https/lgl/strverscmp.h new file mode 100644 index 00000000..d95d7b31 --- /dev/null +++ b/src/daemon/https/lgl/strverscmp.h @@ -0,0 +1,24 @@ +/* Compare strings while treating digits characters numerically. + + Copyright (C) 1997, 2003 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1, or (at your option) + any later version. + + This program 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef STRVERSCMP_H_ +# define STRVERSCMP_H_ + +int strverscmp (const char *, const char *); + +#endif /* not STRVERSCMP_H_ */ diff --git a/src/daemon/https/lgl/time_r.c b/src/daemon/https/lgl/time_r.c new file mode 100644 index 00000000..691525df --- /dev/null +++ b/src/daemon/https/lgl/time_r.c @@ -0,0 +1,46 @@ +/* Reentrant time functions like localtime_r. + + Copyright (C) 2003, 2006, 2007 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1, or (at your option) + any later version. + + This program 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Written by Paul Eggert. */ + +#include + +#include + +#include + +static struct tm * +copy_tm_result (struct tm *dest, struct tm const *src) +{ + if (!src) + return 0; + *dest = *src; + return dest; +} + +struct tm * +gmtime_r (time_t const *restrict t, struct tm *restrict tp) +{ + return copy_tm_result (tp, gmtime (t)); +} + +struct tm * +localtime_r (time_t const *restrict t, struct tm *restrict tp) +{ + return copy_tm_result (tp, localtime (t)); +} diff --git a/src/daemon/https/lgl/vasnprintf.c b/src/daemon/https/lgl/vasnprintf.c new file mode 100644 index 00000000..d247b592 --- /dev/null +++ b/src/daemon/https/lgl/vasnprintf.c @@ -0,0 +1,4825 @@ +/* vsprintf with automatic memory allocation. + Copyright (C) 1999, 2002-2007 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1, or (at your option) + any later version. + + This program 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* This file can be parametrized with the following macros: + VASNPRINTF The name of the function being defined. + FCHAR_T The element type of the format string. + DCHAR_T The element type of the destination (result) string. + FCHAR_T_ONLY_ASCII Set to 1 to enable verification that all characters + in the format string are ASCII. MUST be set if + FCHAR_T and DCHAR_T are not the same type. + DIRECTIVE Structure denoting a format directive. + Depends on FCHAR_T. + DIRECTIVES Structure denoting the set of format directives of a + format string. Depends on FCHAR_T. + PRINTF_PARSE Function that parses a format string. + Depends on FCHAR_T. + DCHAR_CPY memcpy like function for DCHAR_T[] arrays. + DCHAR_SET memset like function for DCHAR_T[] arrays. + DCHAR_MBSNLEN mbsnlen like function for DCHAR_T[] arrays. + SNPRINTF The system's snprintf (or similar) function. + This may be either snprintf or swprintf. + TCHAR_T The element type of the argument and result string + of the said SNPRINTF function. This may be either + char or wchar_t. The code exploits that + sizeof (TCHAR_T) | sizeof (DCHAR_T) and + alignof (TCHAR_T) <= alignof (DCHAR_T). + DCHAR_IS_TCHAR Set to 1 if DCHAR_T and TCHAR_T are the same type. + DCHAR_CONV_FROM_ENCODING A function to convert from char[] to DCHAR[]. + DCHAR_IS_UINT8_T Set to 1 if DCHAR_T is uint8_t. + DCHAR_IS_UINT16_T Set to 1 if DCHAR_T is uint16_t. + DCHAR_IS_UINT32_T Set to 1 if DCHAR_T is uint32_t. */ + +/* Tell glibc's to provide a prototype for snprintf(). + This must come before because may include + , and once has been included, it's too late. */ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE 1 +#endif + +#ifndef VASNPRINTF +# include +#endif +#ifndef IN_LIBINTL +# include +#endif + +/* Specification. */ +#ifndef VASNPRINTF +# if WIDE_CHAR_VERSION +# include "vasnwprintf.h" +# else +# include "vasnprintf.h" +# endif +#endif + +#include /* localeconv() */ +#include /* snprintf(), sprintf() */ +#include /* abort(), malloc(), realloc(), free() */ +#include /* memcpy(), strlen() */ +#include /* errno */ +#include /* CHAR_BIT */ +#include /* DBL_MAX_EXP, LDBL_MAX_EXP */ +#if HAVE_NL_LANGINFO +# include +#endif +#ifndef VASNPRINTF +# if WIDE_CHAR_VERSION +# include "wprintf-parse.h" +# else +# include "printf-parse.h" +# endif +#endif + +/* Checked size_t computations. */ +#include "xsize.h" + +#if (NEED_PRINTF_DOUBLE || NEED_PRINTF_LONG_DOUBLE) && !defined IN_LIBINTL +# include +# include "float+.h" +#endif + +#if (NEED_PRINTF_DOUBLE || NEED_PRINTF_INFINITE_DOUBLE) && !defined IN_LIBINTL +# include +# include "isnan.h" +#endif + +#if (NEED_PRINTF_LONG_DOUBLE || NEED_PRINTF_INFINITE_LONG_DOUBLE) && !defined IN_LIBINTL +# include +# include "isnanl-nolibm.h" +# include "fpucw.h" +#endif + +#if (NEED_PRINTF_DIRECTIVE_A || NEED_PRINTF_DOUBLE) && !defined IN_LIBINTL +# include +# include "isnan.h" +# include "printf-frexp.h" +#endif + +#if (NEED_PRINTF_DIRECTIVE_A || NEED_PRINTF_LONG_DOUBLE) && !defined IN_LIBINTL +# include +# include "isnanl-nolibm.h" +# include "printf-frexpl.h" +# include "fpucw.h" +#endif + +/* Some systems, like OSF/1 4.0 and Woe32, don't have EOVERFLOW. */ +#ifndef EOVERFLOW +# define EOVERFLOW E2BIG +#endif + +#if HAVE_WCHAR_T +# if HAVE_WCSLEN +# define local_wcslen wcslen +# else + /* Solaris 2.5.1 has wcslen() in a separate library libw.so. To avoid + a dependency towards this library, here is a local substitute. + Define this substitute only once, even if this file is included + twice in the same compilation unit. */ +# ifndef local_wcslen_defined +# define local_wcslen_defined 1 +static size_t +local_wcslen (const wchar_t * s) +{ + const wchar_t *ptr; + + for (ptr = s; *ptr != (wchar_t) 0; ptr++) + ; + return ptr - s; +} +# endif +# endif +#endif + +/* Default parameters. */ +#ifndef VASNPRINTF +# if WIDE_CHAR_VERSION +# define VASNPRINTF vasnwprintf +# define FCHAR_T wchar_t +# define DCHAR_T wchar_t +# define TCHAR_T wchar_t +# define DCHAR_IS_TCHAR 1 +# define DIRECTIVE wchar_t_directive +# define DIRECTIVES wchar_t_directives +# define PRINTF_PARSE wprintf_parse +# define DCHAR_CPY wmemcpy +# else +# define VASNPRINTF vasnprintf +# define FCHAR_T char +# define DCHAR_T char +# define TCHAR_T char +# define DCHAR_IS_TCHAR 1 +# define DIRECTIVE char_directive +# define DIRECTIVES char_directives +# define PRINTF_PARSE printf_parse +# define DCHAR_CPY memcpy +# endif +#endif +#if WIDE_CHAR_VERSION + /* TCHAR_T is wchar_t. */ +# define USE_SNPRINTF 1 +# if HAVE_DECL__SNWPRINTF + /* On Windows, the function swprintf() has a different signature than + on Unix; we use the _snwprintf() function instead. */ +# define SNPRINTF _snwprintf +# else + /* Unix. */ +# define SNPRINTF swprintf +# endif +#else + /* TCHAR_T is char. */ +# /* Use snprintf if it exists under the name 'snprintf' or '_snprintf'. + But don't use it on BeOS, since BeOS snprintf produces no output if the + size argument is >= 0x3000000. */ +# if (HAVE_DECL__SNPRINTF || HAVE_SNPRINTF) && !defined __BEOS__ +# define USE_SNPRINTF 1 +# else +# define USE_SNPRINTF 0 +# endif +# if HAVE_DECL__SNPRINTF + /* Windows. */ +# define SNPRINTF _snprintf +# else + /* Unix. */ +# define SNPRINTF snprintf + /* Here we need to call the native snprintf, not rpl_snprintf. */ +# undef snprintf +# endif +#endif +/* Here we need to call the native sprintf, not rpl_sprintf. */ +#undef sprintf + +#if (NEED_PRINTF_DIRECTIVE_A || NEED_PRINTF_LONG_DOUBLE || NEED_PRINTF_DOUBLE || NEED_PRINTF_INFINITE_DOUBLE) && !defined IN_LIBINTL +/* Determine the decimal-point character according to the current locale. */ +# ifndef decimal_point_char_defined +# define decimal_point_char_defined 1 +static char +decimal_point_char () +{ + const char *point; + /* Determine it in a multithread-safe way. We know nl_langinfo is + multithread-safe on glibc systems, but is not required to be multithread- + safe by POSIX. sprintf(), however, is multithread-safe. localeconv() + is rarely multithread-safe. */ +# if HAVE_NL_LANGINFO && __GLIBC__ + point = nl_langinfo (RADIXCHAR); +# elif 1 + char pointbuf[5]; + sprintf (pointbuf, "%#.0f", 1.0); + point = &pointbuf[1]; +# else + point = localeconv ()->decimal_point; +# endif + /* The decimal point is always a single byte: either '.' or ','. */ + return (point[0] != '\0' ? point[0] : '.'); +} +# endif +#endif + +#if NEED_PRINTF_INFINITE_DOUBLE && !NEED_PRINTF_DOUBLE && !defined IN_LIBINTL + +/* Equivalent to !isfinite(x) || x == 0, but does not require libm. */ +static int +is_infinite_or_zero (double x) +{ + return isnan (x) || x + x == x; +} + +#endif + +#if NEED_PRINTF_INFINITE_LONG_DOUBLE && !NEED_PRINTF_LONG_DOUBLE && !defined IN_LIBINTL + +/* Equivalent to !isfinite(x), but does not require libm. */ +static int +is_infinitel (long double x) +{ + return isnanl (x) || (x + x == x && x != 0.0L); +} + +#endif + +#if (NEED_PRINTF_LONG_DOUBLE || NEED_PRINTF_DOUBLE) && !defined IN_LIBINTL + +/* Converting 'long double' to decimal without rare rounding bugs requires + real bignums. We use the naming conventions of GNU gmp, but vastly simpler + (and slower) algorithms. */ + +typedef unsigned int mp_limb_t; +# define GMP_LIMB_BITS 32 +typedef int mp_limb_verify[2 * + (sizeof (mp_limb_t) * CHAR_BIT == + GMP_LIMB_BITS) - 1]; + +typedef unsigned long long mp_twolimb_t; +# define GMP_TWOLIMB_BITS 64 +typedef int mp_twolimb_verify[2 * + (sizeof (mp_twolimb_t) * CHAR_BIT == + GMP_TWOLIMB_BITS) - 1]; + +/* Representation of a bignum >= 0. */ +typedef struct +{ + size_t nlimbs; + mp_limb_t *limbs; /* Bits in little-endian order, allocated with malloc(). */ +} mpn_t; + +/* Compute the product of two bignums >= 0. + Return the allocated memory in case of success, NULL in case of memory + allocation failure. */ +static void * +multiply (mpn_t src1, mpn_t src2, mpn_t * dest) +{ + const mp_limb_t *p1; + const mp_limb_t *p2; + size_t len1; + size_t len2; + + if (src1.nlimbs <= src2.nlimbs) + { + len1 = src1.nlimbs; + p1 = src1.limbs; + len2 = src2.nlimbs; + p2 = src2.limbs; + } + else + { + len1 = src2.nlimbs; + p1 = src2.limbs; + len2 = src1.nlimbs; + p2 = src1.limbs; + } + /* Now 0 <= len1 <= len2. */ + if (len1 == 0) + { + /* src1 or src2 is zero. */ + dest->nlimbs = 0; + dest->limbs = (mp_limb_t *) malloc (1); + } + else + { + /* Here 1 <= len1 <= len2. */ + size_t dlen; + mp_limb_t *dp; + size_t k, i, j; + + dlen = len1 + len2; + dp = (mp_limb_t *) malloc (dlen * sizeof (mp_limb_t)); + if (dp == NULL) + return NULL; + for (k = len2; k > 0;) + dp[--k] = 0; + for (i = 0; i < len1; i++) + { + mp_limb_t digit1 = p1[i]; + mp_twolimb_t carry = 0; + for (j = 0; j < len2; j++) + { + mp_limb_t digit2 = p2[j]; + carry += (mp_twolimb_t) digit1 *(mp_twolimb_t) digit2; + carry += dp[i + j]; + dp[i + j] = (mp_limb_t) carry; + carry = carry >> GMP_LIMB_BITS; + } + dp[i + len2] = (mp_limb_t) carry; + } + /* Normalise. */ + while (dlen > 0 && dp[dlen - 1] == 0) + dlen--; + dest->nlimbs = dlen; + dest->limbs = dp; + } + return dest->limbs; +} + +/* Compute the quotient of a bignum a >= 0 and a bignum b > 0. + a is written as a = q * b + r with 0 <= r < b. q is the quotient, r + the remainder. + Finally, round-to-even is performed: If r > b/2 or if r = b/2 and q is odd, + q is incremented. + Return the allocated memory in case of success, NULL in case of memory + allocation failure. */ +static void * +divide (mpn_t a, mpn_t b, mpn_t * q) +{ + /* Algorithm: + First normalise a and b: a=[a[m-1],...,a[0]], b=[b[n-1],...,b[0]] + with m>=0 and n>0 (in base beta = 2^GMP_LIMB_BITS). + If m=n=1, perform a single-precision division: + r:=0, j:=m, + while j>0 do + {Here (q[m-1]*beta^(m-1)+...+q[j]*beta^j) * b[0] + r*beta^j = + = a[m-1]*beta^(m-1)+...+a[j]*beta^j und 0<=r=n>1, perform a multiple-precision division: + We have a/b < beta^(m-n+1). + s:=intDsize-1-(hightest bit in b[n-1]), 0<=s=beta/2. + For j=m-n,...,0: {Here 0 <= r < b*beta^(j+1).} + Compute q* : + q* := floor((r[j+n]*beta+r[j+n-1])/b[n-1]). + In case of overflow (q* >= beta) set q* := beta-1. + Compute c2 := ((r[j+n]*beta+r[j+n-1]) - q* * b[n-1])*beta + r[j+n-2] + and c3 := b[n-2] * q*. + {We have 0 <= c2 < 2*beta^2, even 0 <= c2 < beta^2 if no overflow + occurred. Furthermore 0 <= c3 < beta^2. + If there was overflow and + r[j+n]*beta+r[j+n-1] - q* * b[n-1] >= beta, i.e. c2 >= beta^2, + the next test can be skipped.} + While c3 > c2, {Here 0 <= c2 < c3 < beta^2} + Put q* := q* - 1, c2 := c2 + b[n-1]*beta, c3 := c3 - b[n-2]. + If q* > 0: + Put r := r - b * q* * beta^j. In detail: + [r[n+j],...,r[j]] := [r[n+j],...,r[j]] - q* * [b[n-1],...,b[0]]. + hence: u:=0, for i:=0 to n-1 do + u := u + q* * b[i], + r[j+i]:=r[j+i]-(u mod beta) (+ beta, if carry), + u:=u div beta (+ 1, if carry in subtraction) + r[n+j]:=r[n+j]-u. + {Since always u = (q* * [b[i-1],...,b[0]] div beta^i) + 1 + < q* + 1 <= beta, + the carry u does not overflow.} + If a negative carry occurs, put q* := q* - 1 + and [r[n+j],...,r[j]] := [r[n+j],...,r[j]] + [0,b[n-1],...,b[0]]. + Set q[j] := q*. + Normalise [q[m-n],..,q[0]]; this yields the quotient q. + Shift [r[n-1],...,r[0]] right by s bits and normalise; this yields the + rest r. + The room for q[j] can be allocated at the memory location of r[n+j]. + Finally, round-to-even: + Shift r left by 1 bit. + If r > b or if r = b and q[0] is odd, q := q+1. + */ + const mp_limb_t *a_ptr = a.limbs; + size_t a_len = a.nlimbs; + const mp_limb_t *b_ptr = b.limbs; + size_t b_len = b.nlimbs; + mp_limb_t *roomptr; + mp_limb_t *tmp_roomptr = NULL; + mp_limb_t *q_ptr; + size_t q_len; + mp_limb_t *r_ptr; + size_t r_len; + + /* Allocate room for a_len+2 digits. + (Need a_len+1 digits for the real division and 1 more digit for the + final rounding of q.) */ + roomptr = (mp_limb_t *) malloc ((a_len + 2) * sizeof (mp_limb_t)); + if (roomptr == NULL) + return NULL; + + /* Normalise a. */ + while (a_len > 0 && a_ptr[a_len - 1] == 0) + a_len--; + + /* Normalise b. */ + for (;;) + { + if (b_len == 0) + /* Division by zero. */ + abort (); + if (b_ptr[b_len - 1] == 0) + b_len--; + else + break; + } + + /* Here m = a_len >= 0 and n = b_len > 0. */ + + if (a_len < b_len) + { + /* m beta^(m-2) <= a/b < beta^m */ + r_ptr = roomptr; + q_ptr = roomptr + 1; + { + mp_limb_t den = b_ptr[0]; + mp_limb_t remainder = 0; + const mp_limb_t *sourceptr = a_ptr + a_len; + mp_limb_t *destptr = q_ptr + a_len; + size_t count; + for (count = a_len; count > 0; count--) + { + mp_twolimb_t num = + ((mp_twolimb_t) remainder << GMP_LIMB_BITS) | *--sourceptr; + *--destptr = num / den; + remainder = num % den; + } + /* Normalise and store r. */ + if (remainder > 0) + { + r_ptr[0] = remainder; + r_len = 1; + } + else + r_len = 0; + /* Normalise q. */ + q_len = a_len; + if (q_ptr[q_len - 1] == 0) + q_len--; + } + } + else + { + /* n>1: multiple precision division. + beta^(m-1) <= a < beta^m, beta^(n-1) <= b < beta^n ==> + beta^(m-n-1) <= a/b < beta^(m-n+1). */ + /* Determine s. */ + size_t s; + { + mp_limb_t msd = b_ptr[b_len - 1]; /* = b[n-1], > 0 */ + s = 31; + if (msd >= 0x10000) + { + msd = msd >> 16; + s -= 16; + } + if (msd >= 0x100) + { + msd = msd >> 8; + s -= 8; + } + if (msd >= 0x10) + { + msd = msd >> 4; + s -= 4; + } + if (msd >= 0x4) + { + msd = msd >> 2; + s -= 2; + } + if (msd >= 0x2) + { + msd = msd >> 1; + s -= 1; + } + } + /* 0 <= s < GMP_LIMB_BITS. + Copy b, shifting it left by s bits. */ + if (s > 0) + { + tmp_roomptr = (mp_limb_t *) malloc (b_len * sizeof (mp_limb_t)); + if (tmp_roomptr == NULL) + { + free (roomptr); + return NULL; + } + { + const mp_limb_t *sourceptr = b_ptr; + mp_limb_t *destptr = tmp_roomptr; + mp_twolimb_t accu = 0; + size_t count; + for (count = b_len; count > 0; count--) + { + accu += (mp_twolimb_t) * sourceptr++ << s; + *destptr++ = (mp_limb_t) accu; + accu = accu >> GMP_LIMB_BITS; + } + /* accu must be zero, since that was how s was determined. */ + if (accu != 0) + abort (); + } + b_ptr = tmp_roomptr; + } + /* Copy a, shifting it left by s bits, yields r. + Memory layout: + At the beginning: r = roomptr[0..a_len], + at the end: r = roomptr[0..b_len-1], q = roomptr[b_len..a_len] */ + r_ptr = roomptr; + if (s == 0) + { + memcpy (r_ptr, a_ptr, a_len * sizeof (mp_limb_t)); + r_ptr[a_len] = 0; + } + else + { + const mp_limb_t *sourceptr = a_ptr; + mp_limb_t *destptr = r_ptr; + mp_twolimb_t accu = 0; + size_t count; + for (count = a_len; count > 0; count--) + { + accu += (mp_twolimb_t) * sourceptr++ << s; + *destptr++ = (mp_limb_t) accu; + accu = accu >> GMP_LIMB_BITS; + } + *destptr++ = (mp_limb_t) accu; + } + q_ptr = roomptr + b_len; + q_len = a_len - b_len + 1; /* q will have m-n+1 limbs */ + { + size_t j = a_len - b_len; /* m-n */ + mp_limb_t b_msd = b_ptr[b_len - 1]; /* b[n-1] */ + mp_limb_t b_2msd = b_ptr[b_len - 2]; /* b[n-2] */ + mp_twolimb_t b_msdd = /* b[n-1]*beta+b[n-2] */ + ((mp_twolimb_t) b_msd << GMP_LIMB_BITS) | b_2msd; + /* Division loop, traversed m-n+1 times. + j counts down, b is unchanged, beta/2 <= b[n-1] < beta. */ + for (;;) + { + mp_limb_t q_star; + mp_limb_t c1; + if (r_ptr[j + b_len] < b_msd) /* r[j+n] < b[n-1] ? */ + { + /* Divide r[j+n]*beta+r[j+n-1] by b[n-1], no overflow. */ + mp_twolimb_t num = + ((mp_twolimb_t) r_ptr[j + b_len] << GMP_LIMB_BITS) + | r_ptr[j + b_len - 1]; + q_star = num / b_msd; + c1 = num % b_msd; + } + else + { + /* Overflow, hence r[j+n]*beta+r[j+n-1] >= beta*b[n-1]. */ + q_star = (mp_limb_t) ~ (mp_limb_t) 0; /* q* = beta-1 */ + /* Test whether r[j+n]*beta+r[j+n-1] - (beta-1)*b[n-1] >= beta + <==> r[j+n]*beta+r[j+n-1] + b[n-1] >= beta*b[n-1]+beta + <==> b[n-1] < floor((r[j+n]*beta+r[j+n-1]+b[n-1])/beta) + {<= beta !}. + If yes, jump directly to the subtraction loop. + (Otherwise, r[j+n]*beta+r[j+n-1] - (beta-1)*b[n-1] < beta + <==> floor((r[j+n]*beta+r[j+n-1]+b[n-1])/beta) = b[n-1] ) */ + if (r_ptr[j + b_len] > b_msd + || (c1 = r_ptr[j + b_len - 1] + b_msd) < b_msd) + /* r[j+n] >= b[n-1]+1 or + r[j+n] = b[n-1] and the addition r[j+n-1]+b[n-1] gives a + carry. */ + goto subtract; + } + /* q_star = q*, + c1 = (r[j+n]*beta+r[j+n-1]) - q* * b[n-1] (>=0, 0, decrease it by + b[n-1]*beta+b[n-2]. Because of b[n-1]*beta+b[n-2] >= beta^2/2 + this can happen only twice. */ + if (c3 > c2) + { + q_star = q_star - 1; /* q* := q* - 1 */ + if (c3 - c2 > b_msdd) + q_star = q_star - 1; /* q* := q* - 1 */ + } + } + if (q_star > 0) + subtract: + { + /* Subtract r := r - b * q* * beta^j. */ + mp_limb_t cr; + { + const mp_limb_t *sourceptr = b_ptr; + mp_limb_t *destptr = r_ptr + j; + mp_twolimb_t carry = 0; + size_t count; + for (count = b_len; count > 0; count--) + { + /* Here 0 <= carry <= q*. */ + carry = + carry + + (mp_twolimb_t) q_star *(mp_twolimb_t) * sourceptr++ + + (mp_limb_t) ~ (*destptr); + /* Here 0 <= carry <= beta*q* + beta-1. */ + *destptr++ = ~(mp_limb_t) carry; + carry = carry >> GMP_LIMB_BITS; /* <= q* */ + } + cr = (mp_limb_t) carry; + } + /* Subtract cr from r_ptr[j + b_len], then forget about + r_ptr[j + b_len]. */ + if (cr > r_ptr[j + b_len]) + { + /* Subtraction gave a carry. */ + q_star = q_star - 1; /* q* := q* - 1 */ + /* Add b back. */ + { + const mp_limb_t *sourceptr = b_ptr; + mp_limb_t *destptr = r_ptr + j; + mp_limb_t carry = 0; + size_t count; + for (count = b_len; count > 0; count--) + { + mp_limb_t source1 = *sourceptr++; + mp_limb_t source2 = *destptr; + *destptr++ = source1 + source2 + carry; + carry = + (carry + ? source1 >= (mp_limb_t) ~ source2 + : source1 > (mp_limb_t) ~ source2); + } + } + /* Forget about the carry and about r[j+n]. */ + } + } + /* q* is determined. Store it as q[j]. */ + q_ptr[j] = q_star; + if (j == 0) + break; + j--; + } + } + r_len = b_len; + /* Normalise q. */ + if (q_ptr[q_len - 1] == 0) + q_len--; +# if 0 /* Not needed here, since we need r only to compare it with b/2, and + b is shifted left by s bits. */ + /* Shift r right by s bits. */ + if (s > 0) + { + mp_limb_t ptr = r_ptr + r_len; + mp_twolimb_t accu = 0; + size_t count; + for (count = r_len; count > 0; count--) + { + accu = (mp_twolimb_t) (mp_limb_t) accu << GMP_LIMB_BITS; + accu += (mp_twolimb_t) * --ptr << (GMP_LIMB_BITS - s); + *ptr = (mp_limb_t) (accu >> GMP_LIMB_BITS); + } + } +# endif + /* Normalise r. */ + while (r_len > 0 && r_ptr[r_len - 1] == 0) + r_len--; + } + /* Compare r << 1 with b. */ + if (r_len > b_len) + goto increment_q; + { + size_t i; + for (i = b_len;;) + { + mp_limb_t r_i = + (i <= r_len && i > 0 ? r_ptr[i - 1] >> (GMP_LIMB_BITS - 1) : 0) + | (i < r_len ? r_ptr[i] << 1 : 0); + mp_limb_t b_i = (i < b_len ? b_ptr[i] : 0); + if (r_i > b_i) + goto increment_q; + if (r_i < b_i) + goto keep_q; + if (i == 0) + break; + i--; + } + } + if (q_len > 0 && ((q_ptr[0] & 1) != 0)) + /* q is odd. */ + increment_q: + { + size_t i; + for (i = 0; i < q_len; i++) + if (++(q_ptr[i]) != 0) + goto keep_q; + q_ptr[q_len++] = 1; + } +keep_q: + if (tmp_roomptr != NULL) + free (tmp_roomptr); + q->limbs = q_ptr; + q->nlimbs = q_len; + return roomptr; +} + +/* Convert a bignum a >= 0, multiplied with 10^extra_zeroes, to decimal + representation. + Destroys the contents of a. + Return the allocated memory - containing the decimal digits in low-to-high + order, terminated with a NUL character - in case of success, NULL in case + of memory allocation failure. */ +static char * +convert_to_decimal (mpn_t a, size_t extra_zeroes) +{ + mp_limb_t *a_ptr = a.limbs; + size_t a_len = a.nlimbs; + /* 0.03345 is slightly larger than log(2)/(9*log(10)). */ + size_t c_len = 9 * ((size_t) (a_len * (GMP_LIMB_BITS * 0.03345f)) + 1); + char *c_ptr = (char *) malloc (xsum (c_len, extra_zeroes)); + if (c_ptr != NULL) + { + char *d_ptr = c_ptr; + for (; extra_zeroes > 0; extra_zeroes--) + *d_ptr++ = '0'; + while (a_len > 0) + { + /* Divide a by 10^9, in-place. */ + mp_limb_t remainder = 0; + mp_limb_t *ptr = a_ptr + a_len; + size_t count; + for (count = a_len; count > 0; count--) + { + mp_twolimb_t num = + ((mp_twolimb_t) remainder << GMP_LIMB_BITS) | *--ptr; + *ptr = num / 1000000000; + remainder = num % 1000000000; + } + /* Store the remainder as 9 decimal digits. */ + for (count = 9; count > 0; count--) + { + *d_ptr++ = '0' + (remainder % 10); + remainder = remainder / 10; + } + /* Normalize a. */ + if (a_ptr[a_len - 1] == 0) + a_len--; + } + /* Remove leading zeroes. */ + while (d_ptr > c_ptr && d_ptr[-1] == '0') + d_ptr--; + /* But keep at least one zero. */ + if (d_ptr == c_ptr) + *d_ptr++ = '0'; + /* Terminate the string. */ + *d_ptr = '\0'; + } + return c_ptr; +} + +# if NEED_PRINTF_LONG_DOUBLE + +/* Assuming x is finite and >= 0: + write x as x = 2^e * m, where m is a bignum. + Return the allocated memory in case of success, NULL in case of memory + allocation failure. */ +static void * +decode_long_double (long double x, int *ep, mpn_t * mp) +{ + mpn_t m; + int exp; + long double y; + size_t i; + + /* Allocate memory for result. */ + m.nlimbs = (LDBL_MANT_BIT + GMP_LIMB_BITS - 1) / GMP_LIMB_BITS; + m.limbs = (mp_limb_t *) malloc (m.nlimbs * sizeof (mp_limb_t)); + if (m.limbs == NULL) + return NULL; + /* Split into exponential part and mantissa. */ + y = frexpl (x, &exp); + if (!(y >= 0.0L && y < 1.0L)) + abort (); + /* x = 2^exp * y = 2^(exp - LDBL_MANT_BIT) * (y * LDBL_MANT_BIT), and the + latter is an integer. */ + /* Convert the mantissa (y * LDBL_MANT_BIT) to a sequence of limbs. + I'm not sure whether it's safe to cast a 'long double' value between + 2^31 and 2^32 to 'unsigned int', therefore play safe and cast only + 'long double' values between 0 and 2^16 (to 'unsigned int' or 'int', + doesn't matter). */ +# if (LDBL_MANT_BIT % GMP_LIMB_BITS) != 0 +# if (LDBL_MANT_BIT % GMP_LIMB_BITS) > GMP_LIMB_BITS / 2 + { + mp_limb_t hi, lo; + y *= (mp_limb_t) 1 << (LDBL_MANT_BIT % (GMP_LIMB_BITS / 2)); + hi = (int) y; + y -= hi; + if (!(y >= 0.0L && y < 1.0L)) + abort (); + y *= (mp_limb_t) 1 << (GMP_LIMB_BITS / 2); + lo = (int) y; + y -= lo; + if (!(y >= 0.0L && y < 1.0L)) + abort (); + m.limbs[LDBL_MANT_BIT / GMP_LIMB_BITS] = (hi << (GMP_LIMB_BITS / 2)) | lo; + } +# else + { + mp_limb_t d; + y *= (mp_limb_t) 1 << (LDBL_MANT_BIT % GMP_LIMB_BITS); + d = (int) y; + y -= d; + if (!(y >= 0.0L && y < 1.0L)) + abort (); + m.limbs[LDBL_MANT_BIT / GMP_LIMB_BITS] = d; + } +# endif +# endif + for (i = LDBL_MANT_BIT / GMP_LIMB_BITS; i > 0;) + { + mp_limb_t hi, lo; + y *= (mp_limb_t) 1 << (GMP_LIMB_BITS / 2); + hi = (int) y; + y -= hi; + if (!(y >= 0.0L && y < 1.0L)) + abort (); + y *= (mp_limb_t) 1 << (GMP_LIMB_BITS / 2); + lo = (int) y; + y -= lo; + if (!(y >= 0.0L && y < 1.0L)) + abort (); + m.limbs[--i] = (hi << (GMP_LIMB_BITS / 2)) | lo; + } +#if 0 /* On FreeBSD 6.1/x86, 'long double' numbers sometimes have excess + precision. */ + if (!(y == 0.0L)) + abort (); +#endif + /* Normalise. */ + while (m.nlimbs > 0 && m.limbs[m.nlimbs - 1] == 0) + m.nlimbs--; + *mp = m; + *ep = exp - LDBL_MANT_BIT; + return m.limbs; +} + +# endif + +# if NEED_PRINTF_DOUBLE + +/* Assuming x is finite and >= 0: + write x as x = 2^e * m, where m is a bignum. + Return the allocated memory in case of success, NULL in case of memory + allocation failure. */ +static void * +decode_double (double x, int *ep, mpn_t * mp) +{ + mpn_t m; + int exp; + double y; + size_t i; + + /* Allocate memory for result. */ + m.nlimbs = (DBL_MANT_BIT + GMP_LIMB_BITS - 1) / GMP_LIMB_BITS; + m.limbs = (mp_limb_t *) malloc (m.nlimbs * sizeof (mp_limb_t)); + if (m.limbs == NULL) + return NULL; + /* Split into exponential part and mantissa. */ + y = frexp (x, &exp); + if (!(y >= 0.0 && y < 1.0)) + abort (); + /* x = 2^exp * y = 2^(exp - DBL_MANT_BIT) * (y * DBL_MANT_BIT), and the + latter is an integer. */ + /* Convert the mantissa (y * DBL_MANT_BIT) to a sequence of limbs. + I'm not sure whether it's safe to cast a 'double' value between + 2^31 and 2^32 to 'unsigned int', therefore play safe and cast only + 'double' values between 0 and 2^16 (to 'unsigned int' or 'int', + doesn't matter). */ +# if (DBL_MANT_BIT % GMP_LIMB_BITS) != 0 +# if (DBL_MANT_BIT % GMP_LIMB_BITS) > GMP_LIMB_BITS / 2 + { + mp_limb_t hi, lo; + y *= (mp_limb_t) 1 << (DBL_MANT_BIT % (GMP_LIMB_BITS / 2)); + hi = (int) y; + y -= hi; + if (!(y >= 0.0 && y < 1.0)) + abort (); + y *= (mp_limb_t) 1 << (GMP_LIMB_BITS / 2); + lo = (int) y; + y -= lo; + if (!(y >= 0.0 && y < 1.0)) + abort (); + m.limbs[DBL_MANT_BIT / GMP_LIMB_BITS] = (hi << (GMP_LIMB_BITS / 2)) | lo; + } +# else + { + mp_limb_t d; + y *= (mp_limb_t) 1 << (DBL_MANT_BIT % GMP_LIMB_BITS); + d = (int) y; + y -= d; + if (!(y >= 0.0 && y < 1.0)) + abort (); + m.limbs[DBL_MANT_BIT / GMP_LIMB_BITS] = d; + } +# endif +# endif + for (i = DBL_MANT_BIT / GMP_LIMB_BITS; i > 0;) + { + mp_limb_t hi, lo; + y *= (mp_limb_t) 1 << (GMP_LIMB_BITS / 2); + hi = (int) y; + y -= hi; + if (!(y >= 0.0 && y < 1.0)) + abort (); + y *= (mp_limb_t) 1 << (GMP_LIMB_BITS / 2); + lo = (int) y; + y -= lo; + if (!(y >= 0.0 && y < 1.0)) + abort (); + m.limbs[--i] = (hi << (GMP_LIMB_BITS / 2)) | lo; + } + if (!(y == 0.0)) + abort (); + /* Normalise. */ + while (m.nlimbs > 0 && m.limbs[m.nlimbs - 1] == 0) + m.nlimbs--; + *mp = m; + *ep = exp - DBL_MANT_BIT; + return m.limbs; +} + +# endif + +/* Assuming x = 2^e * m is finite and >= 0, and n is an integer: + Returns the decimal representation of round (x * 10^n). + Return the allocated memory - containing the decimal digits in low-to-high + order, terminated with a NUL character - in case of success, NULL in case + of memory allocation failure. */ +static char * +scale10_round_decimal_decoded (int e, mpn_t m, void *memory, int n) +{ + int s; + size_t extra_zeroes; + unsigned int abs_n; + unsigned int abs_s; + mp_limb_t *pow5_ptr; + size_t pow5_len; + unsigned int s_limbs; + unsigned int s_bits; + mpn_t pow5; + mpn_t z; + void *z_memory; + char *digits; + + if (memory == NULL) + return NULL; + /* x = 2^e * m, hence + y = round (2^e * 10^n * m) = round (2^(e+n) * 5^n * m) + = round (2^s * 5^n * m). */ + s = e + n; + extra_zeroes = 0; + /* Factor out a common power of 10 if possible. */ + if (s > 0 && n > 0) + { + extra_zeroes = (s < n ? s : n); + s -= extra_zeroes; + n -= extra_zeroes; + } + /* Here y = round (2^s * 5^n * m) * 10^extra_zeroes. + Before converting to decimal, we need to compute + z = round (2^s * 5^n * m). */ + /* Compute 5^|n|, possibly shifted by |s| bits if n and s have the same + sign. 2.322 is slightly larger than log(5)/log(2). */ + abs_n = (n >= 0 ? n : -n); + abs_s = (s >= 0 ? s : -s); + pow5_ptr = + (mp_limb_t *) + malloc (((int) (abs_n * (2.322f / GMP_LIMB_BITS)) + 1 + + abs_s / GMP_LIMB_BITS + 1) * sizeof (mp_limb_t)); + if (pow5_ptr == NULL) + { + free (memory); + return NULL; + } + /* Initialize with 1. */ + pow5_ptr[0] = 1; + pow5_len = 1; + /* Multiply with 5^|n|. */ + if (abs_n > 0) + { + static mp_limb_t const small_pow5[13 + 1] = { + 1, 5, 25, 125, 625, 3125, 15625, 78125, 390625, 1953125, 9765625, + 48828125, 244140625, 1220703125 + }; + unsigned int n13; + for (n13 = 0; n13 <= abs_n; n13 += 13) + { + mp_limb_t digit1 = small_pow5[n13 + 13 <= abs_n ? 13 : abs_n - n13]; + size_t j; + mp_twolimb_t carry = 0; + for (j = 0; j < pow5_len; j++) + { + mp_limb_t digit2 = pow5_ptr[j]; + carry += (mp_twolimb_t) digit1 *(mp_twolimb_t) digit2; + pow5_ptr[j] = (mp_limb_t) carry; + carry = carry >> GMP_LIMB_BITS; + } + if (carry > 0) + pow5_ptr[pow5_len++] = (mp_limb_t) carry; + } + } + s_limbs = abs_s / GMP_LIMB_BITS; + s_bits = abs_s % GMP_LIMB_BITS; + if (n >= 0 ? s >= 0 : s <= 0) + { + /* Multiply with 2^|s|. */ + if (s_bits > 0) + { + mp_limb_t *ptr = pow5_ptr; + mp_twolimb_t accu = 0; + size_t count; + for (count = pow5_len; count > 0; count--) + { + accu += (mp_twolimb_t) * ptr << s_bits; + *ptr++ = (mp_limb_t) accu; + accu = accu >> GMP_LIMB_BITS; + } + if (accu > 0) + { + *ptr = (mp_limb_t) accu; + pow5_len++; + } + } + if (s_limbs > 0) + { + size_t count; + for (count = pow5_len; count > 0;) + { + count--; + pow5_ptr[s_limbs + count] = pow5_ptr[count]; + } + for (count = s_limbs; count > 0;) + { + count--; + pow5_ptr[count] = 0; + } + pow5_len += s_limbs; + } + pow5.limbs = pow5_ptr; + pow5.nlimbs = pow5_len; + if (n >= 0) + { + /* Multiply m with pow5. No division needed. */ + z_memory = multiply (m, pow5, &z); + } + else + { + /* Divide m by pow5 and round. */ + z_memory = divide (m, pow5, &z); + } + } + else + { + pow5.limbs = pow5_ptr; + pow5.nlimbs = pow5_len; + if (n >= 0) + { + /* n >= 0, s < 0. + Multiply m with pow5, then divide by 2^|s|. */ + mpn_t numerator; + mpn_t denominator; + void *tmp_memory; + tmp_memory = multiply (m, pow5, &numerator); + if (tmp_memory == NULL) + { + free (pow5_ptr); + free (memory); + return NULL; + } + /* Construct 2^|s|. */ + { + mp_limb_t *ptr = pow5_ptr + pow5_len; + size_t i; + for (i = 0; i < s_limbs; i++) + ptr[i] = 0; + ptr[s_limbs] = (mp_limb_t) 1 << s_bits; + denominator.limbs = ptr; + denominator.nlimbs = s_limbs + 1; + } + z_memory = divide (numerator, denominator, &z); + free (tmp_memory); + } + else + { + /* n < 0, s > 0. + Multiply m with 2^s, then divide by pow5. */ + mpn_t numerator; + mp_limb_t *num_ptr; + num_ptr = (mp_limb_t *) malloc ((m.nlimbs + s_limbs + 1) + * sizeof (mp_limb_t)); + if (num_ptr == NULL) + { + free (pow5_ptr); + free (memory); + return NULL; + } + { + mp_limb_t *destptr = num_ptr; + { + size_t i; + for (i = 0; i < s_limbs; i++) + *destptr++ = 0; + } + if (s_bits > 0) + { + const mp_limb_t *sourceptr = m.limbs; + mp_twolimb_t accu = 0; + size_t count; + for (count = m.nlimbs; count > 0; count--) + { + accu += (mp_twolimb_t) * sourceptr++ << s_bits; + *destptr++ = (mp_limb_t) accu; + accu = accu >> GMP_LIMB_BITS; + } + if (accu > 0) + *destptr++ = (mp_limb_t) accu; + } + else + { + const mp_limb_t *sourceptr = m.limbs; + size_t count; + for (count = m.nlimbs; count > 0; count--) + *destptr++ = *sourceptr++; + } + numerator.limbs = num_ptr; + numerator.nlimbs = destptr - num_ptr; + } + z_memory = divide (numerator, pow5, &z); + free (num_ptr); + } + } + free (pow5_ptr); + free (memory); + + /* Here y = round (x * 10^n) = z * 10^extra_zeroes. */ + + if (z_memory == NULL) + return NULL; + digits = convert_to_decimal (z, extra_zeroes); + free (z_memory); + return digits; +} + +# if NEED_PRINTF_LONG_DOUBLE + +/* Assuming x is finite and >= 0, and n is an integer: + Returns the decimal representation of round (x * 10^n). + Return the allocated memory - containing the decimal digits in low-to-high + order, terminated with a NUL character - in case of success, NULL in case + of memory allocation failure. */ +static char * +scale10_round_decimal_long_double (long double x, int n) +{ + int e; + mpn_t m; + void *memory = decode_long_double (x, &e, &m); + return scale10_round_decimal_decoded (e, m, memory, n); +} + +# endif + +# if NEED_PRINTF_DOUBLE + +/* Assuming x is finite and >= 0, and n is an integer: + Returns the decimal representation of round (x * 10^n). + Return the allocated memory - containing the decimal digits in low-to-high + order, terminated with a NUL character - in case of success, NULL in case + of memory allocation failure. */ +static char * +scale10_round_decimal_double (double x, int n) +{ + int e; + mpn_t m; + void *memory = decode_double (x, &e, &m); + return scale10_round_decimal_decoded (e, m, memory, n); +} + +# endif + +# if NEED_PRINTF_LONG_DOUBLE + +/* Assuming x is finite and > 0: + Return an approximation for n with 10^n <= x < 10^(n+1). + The approximation is usually the right n, but may be off by 1 sometimes. */ +static int +floorlog10l (long double x) +{ + int exp; + long double y; + double z; + double l; + + /* Split into exponential part and mantissa. */ + y = frexpl (x, &exp); + if (!(y >= 0.0L && y < 1.0L)) + abort (); + if (y == 0.0L) + return INT_MIN; + if (y < 0.5L) + { + while (y < + (1.0L / (1 << (GMP_LIMB_BITS / 2)) / (1 << (GMP_LIMB_BITS / 2)))) + { + y *= 1.0L * (1 << (GMP_LIMB_BITS / 2)) * (1 << (GMP_LIMB_BITS / 2)); + exp -= GMP_LIMB_BITS; + } + if (y < (1.0L / (1 << 16))) + { + y *= 1.0L * (1 << 16); + exp -= 16; + } + if (y < (1.0L / (1 << 8))) + { + y *= 1.0L * (1 << 8); + exp -= 8; + } + if (y < (1.0L / (1 << 4))) + { + y *= 1.0L * (1 << 4); + exp -= 4; + } + if (y < (1.0L / (1 << 2))) + { + y *= 1.0L * (1 << 2); + exp -= 2; + } + if (y < (1.0L / (1 << 1))) + { + y *= 1.0L * (1 << 1); + exp -= 1; + } + } + if (!(y >= 0.5L && y < 1.0L)) + abort (); + /* Compute an approximation for l = log2(x) = exp + log2(y). */ + l = exp; + z = y; + if (z < 0.70710678118654752444) + { + z *= 1.4142135623730950488; + l -= 0.5; + } + if (z < 0.8408964152537145431) + { + z *= 1.1892071150027210667; + l -= 0.25; + } + if (z < 0.91700404320467123175) + { + z *= 1.0905077326652576592; + l -= 0.125; + } + if (z < 0.9576032806985736469) + { + z *= 1.0442737824274138403; + l -= 0.0625; + } + /* Now 0.95 <= z <= 1.01. */ + z = 1 - z; + /* log(1-z) = - z - z^2/2 - z^3/3 - z^4/4 - ... + Four terms are enough to get an approximation with error < 10^-7. */ + l -= z * (1.0 + z * (0.5 + z * ((1.0 / 3) + z * 0.25))); + /* Finally multiply with log(2)/log(10), yields an approximation for + log10(x). */ + l *= 0.30102999566398119523; + /* Round down to the next integer. */ + return (int) l + (l < 0 ? -1 : 0); +} + +# endif + +# if NEED_PRINTF_DOUBLE + +/* Assuming x is finite and > 0: + Return an approximation for n with 10^n <= x < 10^(n+1). + The approximation is usually the right n, but may be off by 1 sometimes. */ +static int +floorlog10 (double x) +{ + int exp; + double y; + double z; + double l; + + /* Split into exponential part and mantissa. */ + y = frexp (x, &exp); + if (!(y >= 0.0 && y < 1.0)) + abort (); + if (y == 0.0) + return INT_MIN; + if (y < 0.5) + { + while (y < + (1.0 / (1 << (GMP_LIMB_BITS / 2)) / (1 << (GMP_LIMB_BITS / 2)))) + { + y *= 1.0 * (1 << (GMP_LIMB_BITS / 2)) * (1 << (GMP_LIMB_BITS / 2)); + exp -= GMP_LIMB_BITS; + } + if (y < (1.0 / (1 << 16))) + { + y *= 1.0 * (1 << 16); + exp -= 16; + } + if (y < (1.0 / (1 << 8))) + { + y *= 1.0 * (1 << 8); + exp -= 8; + } + if (y < (1.0 / (1 << 4))) + { + y *= 1.0 * (1 << 4); + exp -= 4; + } + if (y < (1.0 / (1 << 2))) + { + y *= 1.0 * (1 << 2); + exp -= 2; + } + if (y < (1.0 / (1 << 1))) + { + y *= 1.0 * (1 << 1); + exp -= 1; + } + } + if (!(y >= 0.5 && y < 1.0)) + abort (); + /* Compute an approximation for l = log2(x) = exp + log2(y). */ + l = exp; + z = y; + if (z < 0.70710678118654752444) + { + z *= 1.4142135623730950488; + l -= 0.5; + } + if (z < 0.8408964152537145431) + { + z *= 1.1892071150027210667; + l -= 0.25; + } + if (z < 0.91700404320467123175) + { + z *= 1.0905077326652576592; + l -= 0.125; + } + if (z < 0.9576032806985736469) + { + z *= 1.0442737824274138403; + l -= 0.0625; + } + /* Now 0.95 <= z <= 1.01. */ + z = 1 - z; + /* log(1-z) = - z - z^2/2 - z^3/3 - z^4/4 - ... + Four terms are enough to get an approximation with error < 10^-7. */ + l -= z * (1.0 + z * (0.5 + z * ((1.0 / 3) + z * 0.25))); + /* Finally multiply with log(2)/log(10), yields an approximation for + log10(x). */ + l *= 0.30102999566398119523; + /* Round down to the next integer. */ + return (int) l + (l < 0 ? -1 : 0); +} + +# endif + +#endif + +DCHAR_T * +VASNPRINTF (DCHAR_T * resultbuf, size_t * lengthp, + const FCHAR_T * format, va_list args) +{ + DIRECTIVES d; + arguments a; + + if (PRINTF_PARSE (format, &d, &a) < 0) + /* errno is already set. */ + return NULL; + +#define CLEANUP() \ + free (d.dir); \ + if (a.arg) \ + free (a.arg); + + if (PRINTF_FETCHARGS (args, &a) < 0) + { + CLEANUP (); + errno = EINVAL; + return NULL; + } + + { + size_t buf_neededlength; + TCHAR_T *buf; + TCHAR_T *buf_malloced; + const FCHAR_T *cp; + size_t i; + DIRECTIVE *dp; + /* Output string accumulator. */ + DCHAR_T *result; + size_t allocated; + size_t length; + + /* Allocate a small buffer that will hold a directive passed to + sprintf or snprintf. */ + buf_neededlength = + xsum4 (7, d.max_width_length, d.max_precision_length, 6); +#if HAVE_ALLOCA + if (buf_neededlength < 4000 / sizeof (TCHAR_T)) + { + buf = (TCHAR_T *) alloca (buf_neededlength * sizeof (TCHAR_T)); + buf_malloced = NULL; + } + else +#endif + { + size_t buf_memsize = xtimes (buf_neededlength, sizeof (TCHAR_T)); + if (size_overflow_p (buf_memsize)) + goto out_of_memory_1; + buf = (TCHAR_T *) malloc (buf_memsize); + if (buf == NULL) + goto out_of_memory_1; + buf_malloced = buf; + } + + if (resultbuf != NULL) + { + result = resultbuf; + allocated = *lengthp; + } + else + { + result = NULL; + allocated = 0; + } + length = 0; + /* Invariants: + result is either == resultbuf or == NULL or malloc-allocated. + If length > 0, then result != NULL. */ + + /* Ensures that allocated >= needed. Aborts through a jump to + out_of_memory if needed is SIZE_MAX or otherwise too big. */ +#define ENSURE_ALLOCATION(needed) \ + if ((needed) > allocated) \ + { \ + size_t memory_size; \ + DCHAR_T *memory; \ + \ + allocated = (allocated > 0 ? xtimes (allocated, 2) : 12); \ + if ((needed) > allocated) \ + allocated = (needed); \ + memory_size = xtimes (allocated, sizeof (DCHAR_T)); \ + if (size_overflow_p (memory_size)) \ + goto out_of_memory; \ + if (result == resultbuf || result == NULL) \ + memory = (DCHAR_T *) malloc (memory_size); \ + else \ + memory = (DCHAR_T *) realloc (result, memory_size); \ + if (memory == NULL) \ + goto out_of_memory; \ + if (result == resultbuf && length > 0) \ + DCHAR_CPY (memory, result, length); \ + result = memory; \ + } + + for (cp = format, i = 0, dp = &d.dir[0];; cp = dp->dir_end, i++, dp++) + { + if (cp != dp->dir_start) + { + size_t n = dp->dir_start - cp; + size_t augmented_length = xsum (length, n); + + ENSURE_ALLOCATION (augmented_length); + /* This copies a piece of FCHAR_T[] into a DCHAR_T[]. Here we + need that the format string contains only ASCII characters + if FCHAR_T and DCHAR_T are not the same type. */ + if (sizeof (FCHAR_T) == sizeof (DCHAR_T)) + { + DCHAR_CPY (result + length, (const DCHAR_T *) cp, n); + length = augmented_length; + } + else + { + do + result[length++] = (unsigned char) *cp++; + while (--n > 0); + } + } + if (i == d.count) + break; + + /* Execute a single directive. */ + if (dp->conversion == '%') + { + size_t augmented_length; + + if (!(dp->arg_index == ARG_NONE)) + abort (); + augmented_length = xsum (length, 1); + ENSURE_ALLOCATION (augmented_length); + result[length] = '%'; + length = augmented_length; + } + else + { + if (!(dp->arg_index != ARG_NONE)) + abort (); + + if (dp->conversion == 'n') + { + switch (a.arg[dp->arg_index].type) + { + case TYPE_COUNT_SCHAR_POINTER: + *a.arg[dp->arg_index].a.a_count_schar_pointer = length; + break; + case TYPE_COUNT_SHORT_POINTER: + *a.arg[dp->arg_index].a.a_count_short_pointer = length; + break; + case TYPE_COUNT_INT_POINTER: + *a.arg[dp->arg_index].a.a_count_int_pointer = length; + break; + case TYPE_COUNT_LONGINT_POINTER: + *a.arg[dp->arg_index].a.a_count_longint_pointer = length; + break; +#if HAVE_LONG_LONG_INT + case TYPE_COUNT_LONGLONGINT_POINTER: + *a.arg[dp->arg_index].a.a_count_longlongint_pointer = + length; + break; +#endif + default: + abort (); + } + } +#if ENABLE_UNISTDIO + /* The unistdio extensions. */ + else if (dp->conversion == 'U') + { + arg_type type = a.arg[dp->arg_index].type; + int flags = dp->flags; + int has_width; + size_t width; + int has_precision; + size_t precision; + + has_width = 0; + width = 0; + if (dp->width_start != dp->width_end) + { + if (dp->width_arg_index != ARG_NONE) + { + int arg; + + if (!(a.arg[dp->width_arg_index].type == TYPE_INT)) + abort (); + arg = a.arg[dp->width_arg_index].a.a_int; + if (arg < 0) + { + /* "A negative field width is taken as a '-' flag + followed by a positive field width." */ + flags |= FLAG_LEFT; + width = (unsigned int) (-arg); + } + else + width = arg; + } + else + { + const FCHAR_T *digitp = dp->width_start; + + do + width = xsum (xtimes (width, 10), *digitp++ - '0'); + while (digitp != dp->width_end); + } + has_width = 1; + } + + has_precision = 0; + precision = 0; + if (dp->precision_start != dp->precision_end) + { + if (dp->precision_arg_index != ARG_NONE) + { + int arg; + + if (! + (a.arg[dp->precision_arg_index].type == TYPE_INT)) + abort (); + arg = a.arg[dp->precision_arg_index].a.a_int; + /* "A negative precision is taken as if the precision + were omitted." */ + if (arg >= 0) + { + precision = arg; + has_precision = 1; + } + } + else + { + const FCHAR_T *digitp = dp->precision_start + 1; + + precision = 0; + while (digitp != dp->precision_end) + precision = + xsum (xtimes (precision, 10), *digitp++ - '0'); + has_precision = 1; + } + } + + switch (type) + { + case TYPE_U8_STRING: + { + const uint8_t *arg = a.arg[dp->arg_index].a.a_u8_string; + const uint8_t *arg_end; + size_t characters; + + if (has_precision) + { + /* Use only PRECISION characters, from the left. */ + arg_end = arg; + characters = 0; + for (; precision > 0; precision--) + { + int count = u8_strmblen (arg_end); + if (count == 0) + break; + if (count < 0) + { + if (! + (result == resultbuf || result == NULL)) + free (result); + if (buf_malloced != NULL) + free (buf_malloced); + CLEANUP (); + errno = EILSEQ; + return NULL; + } + arg_end += count; + characters++; + } + } + else if (has_width) + { + /* Use the entire string, and count the number of + characters. */ + arg_end = arg; + characters = 0; + for (;;) + { + int count = u8_strmblen (arg_end); + if (count == 0) + break; + if (count < 0) + { + if (! + (result == resultbuf || result == NULL)) + free (result); + if (buf_malloced != NULL) + free (buf_malloced); + CLEANUP (); + errno = EILSEQ; + return NULL; + } + arg_end += count; + characters++; + } + } + else + { + /* Use the entire string. */ + arg_end = arg + u8_strlen (arg); + /* The number of characters doesn't matter. */ + characters = 0; + } + + if (has_width && width > characters + && !(dp->flags & FLAG_LEFT)) + { + size_t n = width - characters; + ENSURE_ALLOCATION (xsum (length, n)); + DCHAR_SET (result + length, ' ', n); + length += n; + } + +# if DCHAR_IS_UINT8_T + { + size_t n = arg_end - arg; + ENSURE_ALLOCATION (xsum (length, n)); + DCHAR_CPY (result + length, arg, n); + length += n; + } +# else + { /* Convert. */ + DCHAR_T *converted = result + length; + size_t converted_len = allocated - length; +# if DCHAR_IS_TCHAR + /* Convert from UTF-8 to locale encoding. */ + if (u8_conv_to_encoding (locale_charset (), + iconveh_question_mark, + arg, arg_end - arg, NULL, + &converted, &converted_len) + < 0) +# else + /* Convert from UTF-8 to UTF-16/UTF-32. */ + converted = + U8_TO_DCHAR (arg, arg_end - arg, + converted, &converted_len); + if (converted == NULL) +# endif + { + int saved_errno = errno; + if (!(result == resultbuf || result == NULL)) + free (result); + if (buf_malloced != NULL) + free (buf_malloced); + CLEANUP (); + errno = saved_errno; + return NULL; + } + if (converted != result + length) + { + ENSURE_ALLOCATION (xsum (length, converted_len)); + DCHAR_CPY (result + length, converted, + converted_len); + free (converted); + } + length += converted_len; + } +# endif + + if (has_width && width > characters + && (dp->flags & FLAG_LEFT)) + { + size_t n = width - characters; + ENSURE_ALLOCATION (xsum (length, n)); + DCHAR_SET (result + length, ' ', n); + length += n; + } + } + break; + + case TYPE_U16_STRING: + { + const uint16_t *arg = + a.arg[dp->arg_index].a.a_u16_string; + const uint16_t *arg_end; + size_t characters; + + if (has_precision) + { + /* Use only PRECISION characters, from the left. */ + arg_end = arg; + characters = 0; + for (; precision > 0; precision--) + { + int count = u16_strmblen (arg_end); + if (count == 0) + break; + if (count < 0) + { + if (! + (result == resultbuf || result == NULL)) + free (result); + if (buf_malloced != NULL) + free (buf_malloced); + CLEANUP (); + errno = EILSEQ; + return NULL; + } + arg_end += count; + characters++; + } + } + else if (has_width) + { + /* Use the entire string, and count the number of + characters. */ + arg_end = arg; + characters = 0; + for (;;) + { + int count = u16_strmblen (arg_end); + if (count == 0) + break; + if (count < 0) + { + if (! + (result == resultbuf || result == NULL)) + free (result); + if (buf_malloced != NULL) + free (buf_malloced); + CLEANUP (); + errno = EILSEQ; + return NULL; + } + arg_end += count; + characters++; + } + } + else + { + /* Use the entire string. */ + arg_end = arg + u16_strlen (arg); + /* The number of characters doesn't matter. */ + characters = 0; + } + + if (has_width && width > characters + && !(dp->flags & FLAG_LEFT)) + { + size_t n = width - characters; + ENSURE_ALLOCATION (xsum (length, n)); + DCHAR_SET (result + length, ' ', n); + length += n; + } + +# if DCHAR_IS_UINT16_T + { + size_t n = arg_end - arg; + ENSURE_ALLOCATION (xsum (length, n)); + DCHAR_CPY (result + length, arg, n); + length += n; + } +# else + { /* Convert. */ + DCHAR_T *converted = result + length; + size_t converted_len = allocated - length; +# if DCHAR_IS_TCHAR + /* Convert from UTF-16 to locale encoding. */ + if (u16_conv_to_encoding (locale_charset (), + iconveh_question_mark, + arg, arg_end - arg, NULL, + &converted, &converted_len) + < 0) +# else + /* Convert from UTF-16 to UTF-8/UTF-32. */ + converted = + U16_TO_DCHAR (arg, arg_end - arg, + converted, &converted_len); + if (converted == NULL) +# endif + { + int saved_errno = errno; + if (!(result == resultbuf || result == NULL)) + free (result); + if (buf_malloced != NULL) + free (buf_malloced); + CLEANUP (); + errno = saved_errno; + return NULL; + } + if (converted != result + length) + { + ENSURE_ALLOCATION (xsum (length, converted_len)); + DCHAR_CPY (result + length, converted, + converted_len); + free (converted); + } + length += converted_len; + } +# endif + + if (has_width && width > characters + && (dp->flags & FLAG_LEFT)) + { + size_t n = width - characters; + ENSURE_ALLOCATION (xsum (length, n)); + DCHAR_SET (result + length, ' ', n); + length += n; + } + } + break; + + case TYPE_U32_STRING: + { + const uint32_t *arg = + a.arg[dp->arg_index].a.a_u32_string; + const uint32_t *arg_end; + size_t characters; + + if (has_precision) + { + /* Use only PRECISION characters, from the left. */ + arg_end = arg; + characters = 0; + for (; precision > 0; precision--) + { + int count = u32_strmblen (arg_end); + if (count == 0) + break; + if (count < 0) + { + if (! + (result == resultbuf || result == NULL)) + free (result); + if (buf_malloced != NULL) + free (buf_malloced); + CLEANUP (); + errno = EILSEQ; + return NULL; + } + arg_end += count; + characters++; + } + } + else if (has_width) + { + /* Use the entire string, and count the number of + characters. */ + arg_end = arg; + characters = 0; + for (;;) + { + int count = u32_strmblen (arg_end); + if (count == 0) + break; + if (count < 0) + { + if (! + (result == resultbuf || result == NULL)) + free (result); + if (buf_malloced != NULL) + free (buf_malloced); + CLEANUP (); + errno = EILSEQ; + return NULL; + } + arg_end += count; + characters++; + } + } + else + { + /* Use the entire string. */ + arg_end = arg + u32_strlen (arg); + /* The number of characters doesn't matter. */ + characters = 0; + } + + if (has_width && width > characters + && !(dp->flags & FLAG_LEFT)) + { + size_t n = width - characters; + ENSURE_ALLOCATION (xsum (length, n)); + DCHAR_SET (result + length, ' ', n); + length += n; + } + +# if DCHAR_IS_UINT32_T + { + size_t n = arg_end - arg; + ENSURE_ALLOCATION (xsum (length, n)); + DCHAR_CPY (result + length, arg, n); + length += n; + } +# else + { /* Convert. */ + DCHAR_T *converted = result + length; + size_t converted_len = allocated - length; +# if DCHAR_IS_TCHAR + /* Convert from UTF-32 to locale encoding. */ + if (u32_conv_to_encoding (locale_charset (), + iconveh_question_mark, + arg, arg_end - arg, NULL, + &converted, &converted_len) + < 0) +# else + /* Convert from UTF-32 to UTF-8/UTF-16. */ + converted = + U32_TO_DCHAR (arg, arg_end - arg, + converted, &converted_len); + if (converted == NULL) +# endif + { + int saved_errno = errno; + if (!(result == resultbuf || result == NULL)) + free (result); + if (buf_malloced != NULL) + free (buf_malloced); + CLEANUP (); + errno = saved_errno; + return NULL; + } + if (converted != result + length) + { + ENSURE_ALLOCATION (xsum (length, converted_len)); + DCHAR_CPY (result + length, converted, + converted_len); + free (converted); + } + length += converted_len; + } +# endif + + if (has_width && width > characters + && (dp->flags & FLAG_LEFT)) + { + size_t n = width - characters; + ENSURE_ALLOCATION (xsum (length, n)); + DCHAR_SET (result + length, ' ', n); + length += n; + } + } + break; + + default: + abort (); + } + } +#endif +#if (NEED_PRINTF_DIRECTIVE_A || NEED_PRINTF_LONG_DOUBLE || NEED_PRINTF_DOUBLE) && !defined IN_LIBINTL + else if ((dp->conversion == 'a' || dp->conversion == 'A') +# if !(NEED_PRINTF_DIRECTIVE_A || (NEED_PRINTF_LONG_DOUBLE && NEED_PRINTF_DOUBLE)) + && (0 +# if NEED_PRINTF_DOUBLE + || a.arg[dp->arg_index].type == TYPE_DOUBLE +# endif +# if NEED_PRINTF_LONG_DOUBLE + || a.arg[dp->arg_index].type == TYPE_LONGDOUBLE +# endif + ) +# endif + ) + { + arg_type type = a.arg[dp->arg_index].type; + int flags = dp->flags; + int has_width; + size_t width; + int has_precision; + size_t precision; + size_t tmp_length; + DCHAR_T tmpbuf[700]; + DCHAR_T *tmp; + DCHAR_T *pad_ptr; + DCHAR_T *p; + + has_width = 0; + width = 0; + if (dp->width_start != dp->width_end) + { + if (dp->width_arg_index != ARG_NONE) + { + int arg; + + if (!(a.arg[dp->width_arg_index].type == TYPE_INT)) + abort (); + arg = a.arg[dp->width_arg_index].a.a_int; + if (arg < 0) + { + /* "A negative field width is taken as a '-' flag + followed by a positive field width." */ + flags |= FLAG_LEFT; + width = (unsigned int) (-arg); + } + else + width = arg; + } + else + { + const FCHAR_T *digitp = dp->width_start; + + do + width = xsum (xtimes (width, 10), *digitp++ - '0'); + while (digitp != dp->width_end); + } + has_width = 1; + } + + has_precision = 0; + precision = 0; + if (dp->precision_start != dp->precision_end) + { + if (dp->precision_arg_index != ARG_NONE) + { + int arg; + + if (! + (a.arg[dp->precision_arg_index].type == TYPE_INT)) + abort (); + arg = a.arg[dp->precision_arg_index].a.a_int; + /* "A negative precision is taken as if the precision + were omitted." */ + if (arg >= 0) + { + precision = arg; + has_precision = 1; + } + } + else + { + const FCHAR_T *digitp = dp->precision_start + 1; + + precision = 0; + while (digitp != dp->precision_end) + precision = + xsum (xtimes (precision, 10), *digitp++ - '0'); + has_precision = 1; + } + } + + /* Allocate a temporary buffer of sufficient size. */ + if (type == TYPE_LONGDOUBLE) + tmp_length = (unsigned int) ((LDBL_DIG + 1) * 0.831 /* decimal -> hexadecimal */ + ) + 1; /* turn floor into ceil */ + else + tmp_length = (unsigned int) ((DBL_DIG + 1) * 0.831 /* decimal -> hexadecimal */ + ) + 1; /* turn floor into ceil */ + if (tmp_length < precision) + tmp_length = precision; + /* Account for sign, decimal point etc. */ + tmp_length = xsum (tmp_length, 12); + + if (tmp_length < width) + tmp_length = width; + + tmp_length = xsum (tmp_length, 1); /* account for trailing NUL */ + + if (tmp_length <= sizeof (tmpbuf) / sizeof (DCHAR_T)) + tmp = tmpbuf; + else + { + size_t tmp_memsize = + xtimes (tmp_length, sizeof (DCHAR_T)); + + if (size_overflow_p (tmp_memsize)) + /* Overflow, would lead to out of memory. */ + goto out_of_memory; + tmp = (DCHAR_T *) malloc (tmp_memsize); + if (tmp == NULL) + /* Out of memory. */ + goto out_of_memory; + } + + pad_ptr = NULL; + p = tmp; + if (type == TYPE_LONGDOUBLE) + { +# if NEED_PRINTF_DIRECTIVE_A || NEED_PRINTF_LONG_DOUBLE + long double arg = a.arg[dp->arg_index].a.a_longdouble; + + if (isnanl (arg)) + { + if (dp->conversion == 'A') + { + *p++ = 'N'; + *p++ = 'A'; + *p++ = 'N'; + } + else + { + *p++ = 'n'; + *p++ = 'a'; + *p++ = 'n'; + } + } + else + { + int sign = 0; + DECL_LONG_DOUBLE_ROUNDING + BEGIN_LONG_DOUBLE_ROUNDING (); + + if (signbit (arg)) /* arg < 0.0L or negative zero */ + { + sign = -1; + arg = -arg; + } + + if (sign < 0) + *p++ = '-'; + else if (flags & FLAG_SHOWSIGN) + *p++ = '+'; + else if (flags & FLAG_SPACE) + *p++ = ' '; + + if (arg > 0.0L && arg + arg == arg) + { + if (dp->conversion == 'A') + { + *p++ = 'I'; + *p++ = 'N'; + *p++ = 'F'; + } + else + { + *p++ = 'i'; + *p++ = 'n'; + *p++ = 'f'; + } + } + else + { + int exponent; + long double mantissa; + + if (arg > 0.0L) + mantissa = printf_frexpl (arg, &exponent); + else + { + exponent = 0; + mantissa = 0.0L; + } + + if (has_precision + && precision < + (unsigned int) ((LDBL_DIG + 1) * 0.831) + 1) + { + /* Round the mantissa. */ + long double tail = mantissa; + size_t q; + + for (q = precision;; q--) + { + int digit = (int) tail; + tail -= digit; + if (q == 0) + { + if (digit & 1 ? tail >= 0.5L : tail > + 0.5L) + tail = 1 - tail; + else + tail = -tail; + break; + } + tail *= 16.0L; + } + if (tail != 0.0L) + for (q = precision; q > 0; q--) + tail *= 0.0625L; + mantissa += tail; + } + + *p++ = '0'; + *p++ = dp->conversion - 'A' + 'X'; + pad_ptr = p; + { + int digit; + + digit = (int) mantissa; + mantissa -= digit; + *p++ = '0' + digit; + if ((flags & FLAG_ALT) + || mantissa > 0.0L || precision > 0) + { + *p++ = decimal_point_char (); + /* This loop terminates because we assume + that FLT_RADIX is a power of 2. */ + while (mantissa > 0.0L) + { + mantissa *= 16.0L; + digit = (int) mantissa; + mantissa -= digit; + *p++ = digit + + (digit < 10 + ? '0' : dp->conversion - 10); + if (precision > 0) + precision--; + } + while (precision > 0) + { + *p++ = '0'; + precision--; + } + } + } + *p++ = dp->conversion - 'A' + 'P'; +# if WIDE_CHAR_VERSION + { + static const wchar_t decimal_format[] = + { '%', '+', 'd', '\0' }; + SNPRINTF (p, 6 + 1, decimal_format, exponent); + } + while (*p != '\0') + p++; +# else + if (sizeof (DCHAR_T) == 1) + { + sprintf ((char *) p, "%+d", exponent); + while (*p != '\0') + p++; + } + else + { + char expbuf[6 + 1]; + const char *ep; + sprintf (expbuf, "%+d", exponent); + for (ep = expbuf; (*p = *ep) != '\0'; ep++) + p++; + } +# endif + } + + END_LONG_DOUBLE_ROUNDING (); + } +# else + abort (); +# endif + } + else + { +# if NEED_PRINTF_DIRECTIVE_A || NEED_PRINTF_DOUBLE + double arg = a.arg[dp->arg_index].a.a_double; + + if (isnan (arg)) + { + if (dp->conversion == 'A') + { + *p++ = 'N'; + *p++ = 'A'; + *p++ = 'N'; + } + else + { + *p++ = 'n'; + *p++ = 'a'; + *p++ = 'n'; + } + } + else + { + int sign = 0; + + if (signbit (arg)) /* arg < 0.0 or negative zero */ + { + sign = -1; + arg = -arg; + } + + if (sign < 0) + *p++ = '-'; + else if (flags & FLAG_SHOWSIGN) + *p++ = '+'; + else if (flags & FLAG_SPACE) + *p++ = ' '; + + if (arg > 0.0 && arg + arg == arg) + { + if (dp->conversion == 'A') + { + *p++ = 'I'; + *p++ = 'N'; + *p++ = 'F'; + } + else + { + *p++ = 'i'; + *p++ = 'n'; + *p++ = 'f'; + } + } + else + { + int exponent; + double mantissa; + + if (arg > 0.0) + mantissa = printf_frexp (arg, &exponent); + else + { + exponent = 0; + mantissa = 0.0; + } + + if (has_precision + && precision < + (unsigned int) ((DBL_DIG + 1) * 0.831) + 1) + { + /* Round the mantissa. */ + double tail = mantissa; + size_t q; + + for (q = precision;; q--) + { + int digit = (int) tail; + tail -= digit; + if (q == 0) + { + if (digit & 1 ? tail >= 0.5 : tail > + 0.5) + tail = 1 - tail; + else + tail = -tail; + break; + } + tail *= 16.0; + } + if (tail != 0.0) + for (q = precision; q > 0; q--) + tail *= 0.0625; + mantissa += tail; + } + + *p++ = '0'; + *p++ = dp->conversion - 'A' + 'X'; + pad_ptr = p; + { + int digit; + + digit = (int) mantissa; + mantissa -= digit; + *p++ = '0' + digit; + if ((flags & FLAG_ALT) + || mantissa > 0.0 || precision > 0) + { + *p++ = decimal_point_char (); + /* This loop terminates because we assume + that FLT_RADIX is a power of 2. */ + while (mantissa > 0.0) + { + mantissa *= 16.0; + digit = (int) mantissa; + mantissa -= digit; + *p++ = digit + + (digit < 10 + ? '0' : dp->conversion - 10); + if (precision > 0) + precision--; + } + while (precision > 0) + { + *p++ = '0'; + precision--; + } + } + } + *p++ = dp->conversion - 'A' + 'P'; +# if WIDE_CHAR_VERSION + { + static const wchar_t decimal_format[] = + { '%', '+', 'd', '\0' }; + SNPRINTF (p, 6 + 1, decimal_format, exponent); + } + while (*p != '\0') + p++; +# else + if (sizeof (DCHAR_T) == 1) + { + sprintf ((char *) p, "%+d", exponent); + while (*p != '\0') + p++; + } + else + { + char expbuf[6 + 1]; + const char *ep; + sprintf (expbuf, "%+d", exponent); + for (ep = expbuf; (*p = *ep) != '\0'; ep++) + p++; + } +# endif + } + } +# else + abort (); +# endif + } + /* The generated string now extends from tmp to p, with the + zero padding insertion point being at pad_ptr. */ + if (has_width && p - tmp < width) + { + size_t pad = width - (p - tmp); + DCHAR_T *end = p + pad; + + if (flags & FLAG_LEFT) + { + /* Pad with spaces on the right. */ + for (; pad > 0; pad--) + *p++ = ' '; + } + else if ((flags & FLAG_ZERO) && pad_ptr != NULL) + { + /* Pad with zeroes. */ + DCHAR_T *q = end; + + while (p > pad_ptr) + *--q = *--p; + for (; pad > 0; pad--) + *p++ = '0'; + } + else + { + /* Pad with spaces on the left. */ + DCHAR_T *q = end; + + while (p > tmp) + *--q = *--p; + for (; pad > 0; pad--) + *p++ = ' '; + } + + p = end; + } + + { + size_t count = p - tmp; + + if (count >= tmp_length) + /* tmp_length was incorrectly calculated - fix the + code above! */ + abort (); + + /* Make room for the result. */ + if (count >= allocated - length) + { + size_t n = xsum (length, count); + + ENSURE_ALLOCATION (n); + } + + /* Append the result. */ + memcpy (result + length, tmp, count * sizeof (DCHAR_T)); + if (tmp != tmpbuf) + free (tmp); + length += count; + } + } +#endif +#if (NEED_PRINTF_INFINITE_DOUBLE || NEED_PRINTF_DOUBLE || NEED_PRINTF_INFINITE_LONG_DOUBLE || NEED_PRINTF_LONG_DOUBLE) && !defined IN_LIBINTL + else if ((dp->conversion == 'f' || dp->conversion == 'F' + || dp->conversion == 'e' || dp->conversion == 'E' + || dp->conversion == 'g' || dp->conversion == 'G' + || dp->conversion == 'a' || dp->conversion == 'A') && (0 +# if NEED_PRINTF_DOUBLE + || + a. + arg + [dp-> + arg_index]. + type + == + TYPE_DOUBLE +# elif NEED_PRINTF_INFINITE_DOUBLE + || + (a. + arg + [dp-> + arg_index]. + type + == + TYPE_DOUBLE + /* The systems (mingw) which produce wrong output + for Inf, -Inf, and NaN also do so for -0.0. + Therefore we treat this case here as well. */ + && + is_infinite_or_zero + (a. + arg + [dp-> + arg_index]. + a. + a_double)) +# endif +# if NEED_PRINTF_LONG_DOUBLE + || + a. + arg + [dp-> + arg_index]. + type + == + TYPE_LONGDOUBLE +# elif NEED_PRINTF_INFINITE_LONG_DOUBLE + || + (a. + arg + [dp-> + arg_index]. + type + == + TYPE_LONGDOUBLE + /* Some systems produce wrong output for Inf, + -Inf, and NaN. */ + && + is_infinitel + (a. + arg + [dp-> + arg_index]. + a. + a_longdouble)) +# endif + )) + { +# if (NEED_PRINTF_DOUBLE || NEED_PRINTF_INFINITE_DOUBLE) && (NEED_PRINTF_LONG_DOUBLE || NEED_PRINTF_INFINITE_LONG_DOUBLE) + arg_type type = a.arg[dp->arg_index].type; +# endif + int flags = dp->flags; + int has_width; + size_t width; + int has_precision; + size_t precision; + size_t tmp_length; + DCHAR_T tmpbuf[700]; + DCHAR_T *tmp; + DCHAR_T *pad_ptr; + DCHAR_T *p; + + has_width = 0; + width = 0; + if (dp->width_start != dp->width_end) + { + if (dp->width_arg_index != ARG_NONE) + { + int arg; + + if (!(a.arg[dp->width_arg_index].type == TYPE_INT)) + abort (); + arg = a.arg[dp->width_arg_index].a.a_int; + if (arg < 0) + { + /* "A negative field width is taken as a '-' flag + followed by a positive field width." */ + flags |= FLAG_LEFT; + width = (unsigned int) (-arg); + } + else + width = arg; + } + else + { + const FCHAR_T *digitp = dp->width_start; + + do + width = xsum (xtimes (width, 10), *digitp++ - '0'); + while (digitp != dp->width_end); + } + has_width = 1; + } + + has_precision = 0; + precision = 0; + if (dp->precision_start != dp->precision_end) + { + if (dp->precision_arg_index != ARG_NONE) + { + int arg; + + if (! + (a.arg[dp->precision_arg_index].type == TYPE_INT)) + abort (); + arg = a.arg[dp->precision_arg_index].a.a_int; + /* "A negative precision is taken as if the precision + were omitted." */ + if (arg >= 0) + { + precision = arg; + has_precision = 1; + } + } + else + { + const FCHAR_T *digitp = dp->precision_start + 1; + + precision = 0; + while (digitp != dp->precision_end) + precision = + xsum (xtimes (precision, 10), *digitp++ - '0'); + has_precision = 1; + } + } + + /* POSIX specifies the default precision to be 6 for %f, %F, + %e, %E, but not for %g, %G. Implementations appear to use + the same default precision also for %g, %G. */ + if (!has_precision) + precision = 6; + + /* Allocate a temporary buffer of sufficient size. */ +# if NEED_PRINTF_DOUBLE && NEED_PRINTF_LONG_DOUBLE + tmp_length = + (type == TYPE_LONGDOUBLE ? LDBL_DIG + 1 : DBL_DIG + 1); +# elif NEED_PRINTF_INFINITE_DOUBLE && NEED_PRINTF_LONG_DOUBLE + tmp_length = (type == TYPE_LONGDOUBLE ? LDBL_DIG + 1 : 0); +# elif NEED_PRINTF_LONG_DOUBLE + tmp_length = LDBL_DIG + 1; +# elif NEED_PRINTF_DOUBLE + tmp_length = DBL_DIG + 1; +# else + tmp_length = 0; +# endif + if (tmp_length < precision) + tmp_length = precision; +# if NEED_PRINTF_LONG_DOUBLE +# if NEED_PRINTF_DOUBLE || NEED_PRINTF_INFINITE_DOUBLE + if (type == TYPE_LONGDOUBLE) +# endif + if (dp->conversion == 'f' || dp->conversion == 'F') + { + long double arg = a.arg[dp->arg_index].a.a_longdouble; + if (!(isnanl (arg) || arg + arg == arg)) + { + /* arg is finite and nonzero. */ + int exponent = floorlog10l (arg < 0 ? -arg : arg); + if (exponent >= 0 + && tmp_length < exponent + precision) + tmp_length = exponent + precision; + } + } +# endif +# if NEED_PRINTF_DOUBLE +# if NEED_PRINTF_LONG_DOUBLE || NEED_PRINTF_INFINITE_LONG_DOUBLE + if (type == TYPE_DOUBLE) +# endif + if (dp->conversion == 'f' || dp->conversion == 'F') + { + double arg = a.arg[dp->arg_index].a.a_double; + if (!(isnan (arg) || arg + arg == arg)) + { + /* arg is finite and nonzero. */ + int exponent = floorlog10 (arg < 0 ? -arg : arg); + if (exponent >= 0 + && tmp_length < exponent + precision) + tmp_length = exponent + precision; + } + } +# endif + /* Account for sign, decimal point etc. */ + tmp_length = xsum (tmp_length, 12); + + if (tmp_length < width) + tmp_length = width; + + tmp_length = xsum (tmp_length, 1); /* account for trailing NUL */ + + if (tmp_length <= sizeof (tmpbuf) / sizeof (DCHAR_T)) + tmp = tmpbuf; + else + { + size_t tmp_memsize = + xtimes (tmp_length, sizeof (DCHAR_T)); + + if (size_overflow_p (tmp_memsize)) + /* Overflow, would lead to out of memory. */ + goto out_of_memory; + tmp = (DCHAR_T *) malloc (tmp_memsize); + if (tmp == NULL) + /* Out of memory. */ + goto out_of_memory; + } + + pad_ptr = NULL; + p = tmp; + +# if NEED_PRINTF_LONG_DOUBLE || NEED_PRINTF_INFINITE_LONG_DOUBLE +# if NEED_PRINTF_DOUBLE || NEED_PRINTF_INFINITE_DOUBLE + if (type == TYPE_LONGDOUBLE) +# endif + { + long double arg = a.arg[dp->arg_index].a.a_longdouble; + + if (isnanl (arg)) + { + if (dp->conversion >= 'A' && dp->conversion <= 'Z') + { + *p++ = 'N'; + *p++ = 'A'; + *p++ = 'N'; + } + else + { + *p++ = 'n'; + *p++ = 'a'; + *p++ = 'n'; + } + } + else + { + int sign = 0; + DECL_LONG_DOUBLE_ROUNDING + BEGIN_LONG_DOUBLE_ROUNDING (); + + if (signbit (arg)) /* arg < 0.0L or negative zero */ + { + sign = -1; + arg = -arg; + } + + if (sign < 0) + *p++ = '-'; + else if (flags & FLAG_SHOWSIGN) + *p++ = '+'; + else if (flags & FLAG_SPACE) + *p++ = ' '; + + if (arg > 0.0L && arg + arg == arg) + { + if (dp->conversion >= 'A' + && dp->conversion <= 'Z') + { + *p++ = 'I'; + *p++ = 'N'; + *p++ = 'F'; + } + else + { + *p++ = 'i'; + *p++ = 'n'; + *p++ = 'f'; + } + } + else + { +# if NEED_PRINTF_LONG_DOUBLE + pad_ptr = p; + + if (dp->conversion == 'f' + || dp->conversion == 'F') + { + char *digits; + size_t ndigits; + + digits = + scale10_round_decimal_long_double (arg, + precision); + if (digits == NULL) + { + END_LONG_DOUBLE_ROUNDING (); + goto out_of_memory; + } + ndigits = strlen (digits); + + if (ndigits > precision) + do + { + --ndigits; + *p++ = digits[ndigits]; + } + while (ndigits > precision); + else + *p++ = '0'; + /* Here ndigits <= precision. */ + if ((flags & FLAG_ALT) || precision > 0) + { + *p++ = decimal_point_char (); + for (; precision > ndigits; precision--) + *p++ = '0'; + while (ndigits > 0) + { + --ndigits; + *p++ = digits[ndigits]; + } + } + + free (digits); + } + else if (dp->conversion == 'e' + || dp->conversion == 'E') + { + int exponent; + + if (arg == 0.0L) + { + exponent = 0; + *p++ = '0'; + if ((flags & FLAG_ALT) || precision > 0) + { + *p++ = decimal_point_char (); + for (; precision > 0; precision--) + *p++ = '0'; + } + } + else + { + /* arg > 0.0L. */ + int adjusted; + char *digits; + size_t ndigits; + + exponent = floorlog10l (arg); + adjusted = 0; + for (;;) + { + digits = + scale10_round_decimal_long_double + (arg, (int) precision - exponent); + if (digits == NULL) + { + END_LONG_DOUBLE_ROUNDING (); + goto out_of_memory; + } + ndigits = strlen (digits); + + if (ndigits == precision + 1) + break; + if (ndigits < precision + || ndigits > precision + 2) + /* The exponent was not guessed + precisely enough. */ + abort (); + if (adjusted) + /* None of two values of exponent is + the right one. Prevent an endless + loop. */ + abort (); + free (digits); + if (ndigits == precision) + exponent -= 1; + else + exponent += 1; + adjusted = 1; + } + + /* Here ndigits = precision+1. */ + *p++ = digits[--ndigits]; + if ((flags & FLAG_ALT) || precision > 0) + { + *p++ = decimal_point_char (); + while (ndigits > 0) + { + --ndigits; + *p++ = digits[ndigits]; + } + } + + free (digits); + } + + *p++ = dp->conversion; /* 'e' or 'E' */ +# if WIDE_CHAR_VERSION + { + static const wchar_t decimal_format[] = + { '%', '+', '.', '2', 'd', '\0' }; + SNPRINTF (p, 6 + 1, decimal_format, + exponent); + } + while (*p != '\0') + p++; +# else + if (sizeof (DCHAR_T) == 1) + { + sprintf ((char *) p, "%+.2d", exponent); + while (*p != '\0') + p++; + } + else + { + char expbuf[6 + 1]; + const char *ep; + sprintf (expbuf, "%+.2d", exponent); + for (ep = expbuf; (*p = *ep) != '\0'; + ep++) + p++; + } +# endif + } + else if (dp->conversion == 'g' + || dp->conversion == 'G') + { + if (precision == 0) + precision = 1; + /* precision >= 1. */ + + if (arg == 0.0L) + /* The exponent is 0, >= -4, < precision. + Use fixed-point notation. */ + { + size_t ndigits = precision; + /* Number of trailing zeroes that have to be + dropped. */ + size_t nzeroes = + (flags & FLAG_ALT ? 0 : precision - 1); + + --ndigits; + *p++ = '0'; + if ((flags & FLAG_ALT) + || ndigits > nzeroes) + { + *p++ = decimal_point_char (); + while (ndigits > nzeroes) + { + --ndigits; + *p++ = '0'; + } + } + } + else + { + /* arg > 0.0L. */ + int exponent; + int adjusted; + char *digits; + size_t ndigits; + size_t nzeroes; + + exponent = floorlog10l (arg); + adjusted = 0; + for (;;) + { + digits = + scale10_round_decimal_long_double + (arg, + (int) (precision - 1) - exponent); + if (digits == NULL) + { + END_LONG_DOUBLE_ROUNDING (); + goto out_of_memory; + } + ndigits = strlen (digits); + + if (ndigits == precision) + break; + if (ndigits < precision - 1 + || ndigits > precision + 1) + /* The exponent was not guessed + precisely enough. */ + abort (); + if (adjusted) + /* None of two values of exponent is + the right one. Prevent an endless + loop. */ + abort (); + free (digits); + if (ndigits < precision) + exponent -= 1; + else + exponent += 1; + adjusted = 1; + } + /* Here ndigits = precision. */ + + /* Determine the number of trailing zeroes + that have to be dropped. */ + nzeroes = 0; + if ((flags & FLAG_ALT) == 0) + while (nzeroes < ndigits + && digits[nzeroes] == '0') + nzeroes++; + + /* The exponent is now determined. */ + if (exponent >= -4 + && exponent < (long) precision) + { + /* Fixed-point notation: + max(exponent,0)+1 digits, then the + decimal point, then the remaining + digits without trailing zeroes. */ + if (exponent >= 0) + { + size_t count = exponent + 1; + /* Note: count <= precision = ndigits. */ + for (; count > 0; count--) + *p++ = digits[--ndigits]; + if ((flags & FLAG_ALT) + || ndigits > nzeroes) + { + *p++ = decimal_point_char (); + while (ndigits > nzeroes) + { + --ndigits; + *p++ = digits[ndigits]; + } + } + } + else + { + size_t count = -exponent - 1; + *p++ = '0'; + *p++ = decimal_point_char (); + for (; count > 0; count--) + *p++ = '0'; + while (ndigits > nzeroes) + { + --ndigits; + *p++ = digits[ndigits]; + } + } + } + else + { + /* Exponential notation. */ + *p++ = digits[--ndigits]; + if ((flags & FLAG_ALT) + || ndigits > nzeroes) + { + *p++ = decimal_point_char (); + while (ndigits > nzeroes) + { + --ndigits; + *p++ = digits[ndigits]; + } + } + *p++ = dp->conversion - 'G' + 'E'; /* 'e' or 'E' */ +# if WIDE_CHAR_VERSION + { + static const wchar_t + decimal_format[] = + { '%', '+', '.', '2', 'd', '\0' }; + SNPRINTF (p, 6 + 1, decimal_format, + exponent); + } + while (*p != '\0') + p++; +# else + if (sizeof (DCHAR_T) == 1) + { + sprintf ((char *) p, "%+.2d", + exponent); + while (*p != '\0') + p++; + } + else + { + char expbuf[6 + 1]; + const char *ep; + sprintf (expbuf, "%+.2d", + exponent); + for (ep = expbuf; + (*p = *ep) != '\0'; ep++) + p++; + } +# endif + } + + free (digits); + } + } + else + abort (); +# else + /* arg is finite. */ + abort (); +# endif + } + + END_LONG_DOUBLE_ROUNDING (); + } + } +# if NEED_PRINTF_DOUBLE || NEED_PRINTF_INFINITE_DOUBLE + else +# endif +# endif +# if NEED_PRINTF_DOUBLE || NEED_PRINTF_INFINITE_DOUBLE + { + double arg = a.arg[dp->arg_index].a.a_double; + + if (isnan (arg)) + { + if (dp->conversion >= 'A' && dp->conversion <= 'Z') + { + *p++ = 'N'; + *p++ = 'A'; + *p++ = 'N'; + } + else + { + *p++ = 'n'; + *p++ = 'a'; + *p++ = 'n'; + } + } + else + { + int sign = 0; + + if (signbit (arg)) /* arg < 0.0 or negative zero */ + { + sign = -1; + arg = -arg; + } + + if (sign < 0) + *p++ = '-'; + else if (flags & FLAG_SHOWSIGN) + *p++ = '+'; + else if (flags & FLAG_SPACE) + *p++ = ' '; + + if (arg > 0.0 && arg + arg == arg) + { + if (dp->conversion >= 'A' + && dp->conversion <= 'Z') + { + *p++ = 'I'; + *p++ = 'N'; + *p++ = 'F'; + } + else + { + *p++ = 'i'; + *p++ = 'n'; + *p++ = 'f'; + } + } + else + { +# if NEED_PRINTF_DOUBLE + pad_ptr = p; + + if (dp->conversion == 'f' + || dp->conversion == 'F') + { + char *digits; + size_t ndigits; + + digits = + scale10_round_decimal_double (arg, + precision); + if (digits == NULL) + goto out_of_memory; + ndigits = strlen (digits); + + if (ndigits > precision) + do + { + --ndigits; + *p++ = digits[ndigits]; + } + while (ndigits > precision); + else + *p++ = '0'; + /* Here ndigits <= precision. */ + if ((flags & FLAG_ALT) || precision > 0) + { + *p++ = decimal_point_char (); + for (; precision > ndigits; precision--) + *p++ = '0'; + while (ndigits > 0) + { + --ndigits; + *p++ = digits[ndigits]; + } + } + + free (digits); + } + else if (dp->conversion == 'e' + || dp->conversion == 'E') + { + int exponent; + + if (arg == 0.0) + { + exponent = 0; + *p++ = '0'; + if ((flags & FLAG_ALT) || precision > 0) + { + *p++ = decimal_point_char (); + for (; precision > 0; precision--) + *p++ = '0'; + } + } + else + { + /* arg > 0.0. */ + int adjusted; + char *digits; + size_t ndigits; + + exponent = floorlog10 (arg); + adjusted = 0; + for (;;) + { + digits = + scale10_round_decimal_double (arg, + (int) + precision + - + exponent); + if (digits == NULL) + goto out_of_memory; + ndigits = strlen (digits); + + if (ndigits == precision + 1) + break; + if (ndigits < precision + || ndigits > precision + 2) + /* The exponent was not guessed + precisely enough. */ + abort (); + if (adjusted) + /* None of two values of exponent is + the right one. Prevent an endless + loop. */ + abort (); + free (digits); + if (ndigits == precision) + exponent -= 1; + else + exponent += 1; + adjusted = 1; + } + + /* Here ndigits = precision+1. */ + *p++ = digits[--ndigits]; + if ((flags & FLAG_ALT) || precision > 0) + { + *p++ = decimal_point_char (); + while (ndigits > 0) + { + --ndigits; + *p++ = digits[ndigits]; + } + } + + free (digits); + } + + *p++ = dp->conversion; /* 'e' or 'E' */ +# if WIDE_CHAR_VERSION + { + static const wchar_t decimal_format[] = + /* Produce the same number of exponent digits + as the native printf implementation. */ +# if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ + { '%', '+', '.', '3', 'd', '\0' }; +# else + { '%', '+', '.', '2', 'd', '\0' }; +# endif + SNPRINTF (p, 6 + 1, decimal_format, + exponent); + } + while (*p != '\0') + p++; +# else + { + static const char decimal_format[] = + /* Produce the same number of exponent digits + as the native printf implementation. */ +# if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ + "%+.3d"; +# else + "%+.2d"; +# endif + if (sizeof (DCHAR_T) == 1) + { + sprintf ((char *) p, decimal_format, + exponent); + while (*p != '\0') + p++; + } + else + { + char expbuf[6 + 1]; + const char *ep; + sprintf (expbuf, decimal_format, + exponent); + for (ep = expbuf; (*p = *ep) != '\0'; + ep++) + p++; + } + } +# endif + } + else if (dp->conversion == 'g' + || dp->conversion == 'G') + { + if (precision == 0) + precision = 1; + /* precision >= 1. */ + + if (arg == 0.0) + /* The exponent is 0, >= -4, < precision. + Use fixed-point notation. */ + { + size_t ndigits = precision; + /* Number of trailing zeroes that have to be + dropped. */ + size_t nzeroes = + (flags & FLAG_ALT ? 0 : precision - 1); + + --ndigits; + *p++ = '0'; + if ((flags & FLAG_ALT) + || ndigits > nzeroes) + { + *p++ = decimal_point_char (); + while (ndigits > nzeroes) + { + --ndigits; + *p++ = '0'; + } + } + } + else + { + /* arg > 0.0. */ + int exponent; + int adjusted; + char *digits; + size_t ndigits; + size_t nzeroes; + + exponent = floorlog10 (arg); + adjusted = 0; + for (;;) + { + digits = + scale10_round_decimal_double (arg, + (int) + (precision + - + 1) - + exponent); + if (digits == NULL) + goto out_of_memory; + ndigits = strlen (digits); + + if (ndigits == precision) + break; + if (ndigits < precision - 1 + || ndigits > precision + 1) + /* The exponent was not guessed + precisely enough. */ + abort (); + if (adjusted) + /* None of two values of exponent is + the right one. Prevent an endless + loop. */ + abort (); + free (digits); + if (ndigits < precision) + exponent -= 1; + else + exponent += 1; + adjusted = 1; + } + /* Here ndigits = precision. */ + + /* Determine the number of trailing zeroes + that have to be dropped. */ + nzeroes = 0; + if ((flags & FLAG_ALT) == 0) + while (nzeroes < ndigits + && digits[nzeroes] == '0') + nzeroes++; + + /* The exponent is now determined. */ + if (exponent >= -4 + && exponent < (long) precision) + { + /* Fixed-point notation: + max(exponent,0)+1 digits, then the + decimal point, then the remaining + digits without trailing zeroes. */ + if (exponent >= 0) + { + size_t count = exponent + 1; + /* Note: count <= precision = ndigits. */ + for (; count > 0; count--) + *p++ = digits[--ndigits]; + if ((flags & FLAG_ALT) + || ndigits > nzeroes) + { + *p++ = decimal_point_char (); + while (ndigits > nzeroes) + { + --ndigits; + *p++ = digits[ndigits]; + } + } + } + else + { + size_t count = -exponent - 1; + *p++ = '0'; + *p++ = decimal_point_char (); + for (; count > 0; count--) + *p++ = '0'; + while (ndigits > nzeroes) + { + --ndigits; + *p++ = digits[ndigits]; + } + } + } + else + { + /* Exponential notation. */ + *p++ = digits[--ndigits]; + if ((flags & FLAG_ALT) + || ndigits > nzeroes) + { + *p++ = decimal_point_char (); + while (ndigits > nzeroes) + { + --ndigits; + *p++ = digits[ndigits]; + } + } + *p++ = dp->conversion - 'G' + 'E'; /* 'e' or 'E' */ +# if WIDE_CHAR_VERSION + { + static const wchar_t + decimal_format[] = + /* Produce the same number of exponent digits + as the native printf implementation. */ +# if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ + { '%', '+', '.', '3', 'd', '\0' }; +# else + { '%', '+', '.', '2', 'd', '\0' }; +# endif + SNPRINTF (p, 6 + 1, decimal_format, + exponent); + } + while (*p != '\0') + p++; +# else + { + static const char decimal_format[] = + /* Produce the same number of exponent digits + as the native printf implementation. */ +# if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ + "%+.3d"; +# else + "%+.2d"; +# endif + if (sizeof (DCHAR_T) == 1) + { + sprintf ((char *) p, + decimal_format, + exponent); + while (*p != '\0') + p++; + } + else + { + char expbuf[6 + 1]; + const char *ep; + sprintf (expbuf, decimal_format, + exponent); + for (ep = expbuf; + (*p = *ep) != '\0'; ep++) + p++; + } + } +# endif + } + + free (digits); + } + } + else + abort (); +# else + /* arg is finite. */ + if (!(arg == 0.0)) + abort (); + + pad_ptr = p; + + if (dp->conversion == 'f' + || dp->conversion == 'F') + { + *p++ = '0'; + if ((flags & FLAG_ALT) || precision > 0) + { + *p++ = decimal_point_char (); + for (; precision > 0; precision--) + *p++ = '0'; + } + } + else if (dp->conversion == 'e' + || dp->conversion == 'E') + { + *p++ = '0'; + if ((flags & FLAG_ALT) || precision > 0) + { + *p++ = decimal_point_char (); + for (; precision > 0; precision--) + *p++ = '0'; + } + *p++ = dp->conversion; /* 'e' or 'E' */ + *p++ = '+'; + /* Produce the same number of exponent digits as + the native printf implementation. */ +# if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ + *p++ = '0'; +# endif + *p++ = '0'; + *p++ = '0'; + } + else if (dp->conversion == 'g' + || dp->conversion == 'G') + { + *p++ = '0'; + if (flags & FLAG_ALT) + { + size_t ndigits = + (precision > 0 ? precision - 1 : 0); + *p++ = decimal_point_char (); + for (; ndigits > 0; --ndigits) + *p++ = '0'; + } + } + else + abort (); +# endif + } + } + } +# endif + + /* The generated string now extends from tmp to p, with the + zero padding insertion point being at pad_ptr. */ + if (has_width && p - tmp < width) + { + size_t pad = width - (p - tmp); + DCHAR_T *end = p + pad; + + if (flags & FLAG_LEFT) + { + /* Pad with spaces on the right. */ + for (; pad > 0; pad--) + *p++ = ' '; + } + else if ((flags & FLAG_ZERO) && pad_ptr != NULL) + { + /* Pad with zeroes. */ + DCHAR_T *q = end; + + while (p > pad_ptr) + *--q = *--p; + for (; pad > 0; pad--) + *p++ = '0'; + } + else + { + /* Pad with spaces on the left. */ + DCHAR_T *q = end; + + while (p > tmp) + *--q = *--p; + for (; pad > 0; pad--) + *p++ = ' '; + } + + p = end; + } + + { + size_t count = p - tmp; + + if (count >= tmp_length) + /* tmp_length was incorrectly calculated - fix the + code above! */ + abort (); + + /* Make room for the result. */ + if (count >= allocated - length) + { + size_t n = xsum (length, count); + + ENSURE_ALLOCATION (n); + } + + /* Append the result. */ + memcpy (result + length, tmp, count * sizeof (DCHAR_T)); + if (tmp != tmpbuf) + free (tmp); + length += count; + } + } +#endif + else + { + arg_type type = a.arg[dp->arg_index].type; + int flags = dp->flags; +#if !USE_SNPRINTF || !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION + int has_width; + size_t width; +#endif +#if !USE_SNPRINTF || NEED_PRINTF_UNBOUNDED_PRECISION + int has_precision; + size_t precision; +#endif +#if NEED_PRINTF_UNBOUNDED_PRECISION + int prec_ourselves; +#else +# define prec_ourselves 0 +#endif +#if !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION + int pad_ourselves; +#else +# define pad_ourselves 0 +#endif + TCHAR_T *fbp; + unsigned int prefix_count; + int prefixes[2]; +#if !USE_SNPRINTF + size_t tmp_length; + TCHAR_T tmpbuf[700]; + TCHAR_T *tmp; +#endif + +#if !USE_SNPRINTF || !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION + has_width = 0; + width = 0; + if (dp->width_start != dp->width_end) + { + if (dp->width_arg_index != ARG_NONE) + { + int arg; + + if (!(a.arg[dp->width_arg_index].type == TYPE_INT)) + abort (); + arg = a.arg[dp->width_arg_index].a.a_int; + if (arg < 0) + { + /* "A negative field width is taken as a '-' flag + followed by a positive field width." */ + flags |= FLAG_LEFT; + width = (unsigned int) (-arg); + } + else + width = arg; + } + else + { + const FCHAR_T *digitp = dp->width_start; + + do + width = xsum (xtimes (width, 10), *digitp++ - '0'); + while (digitp != dp->width_end); + } + has_width = 1; + } +#endif + +#if !USE_SNPRINTF || NEED_PRINTF_UNBOUNDED_PRECISION + has_precision = 0; + precision = 6; + if (dp->precision_start != dp->precision_end) + { + if (dp->precision_arg_index != ARG_NONE) + { + int arg; + + if (! + (a.arg[dp->precision_arg_index].type == TYPE_INT)) + abort (); + arg = a.arg[dp->precision_arg_index].a.a_int; + /* "A negative precision is taken as if the precision + were omitted." */ + if (arg >= 0) + { + precision = arg; + has_precision = 1; + } + } + else + { + const FCHAR_T *digitp = dp->precision_start + 1; + + precision = 0; + while (digitp != dp->precision_end) + precision = + xsum (xtimes (precision, 10), *digitp++ - '0'); + has_precision = 1; + } + } +#endif + +#if !USE_SNPRINTF + /* Allocate a temporary buffer of sufficient size for calling + sprintf. */ + { + switch (dp->conversion) + { + + case 'd': + case 'i': + case 'u': +# if HAVE_LONG_LONG_INT + if (type == TYPE_LONGLONGINT + || type == TYPE_ULONGLONGINT) + tmp_length = (unsigned int) (sizeof (unsigned long long) * CHAR_BIT * 0.30103 /* binary -> decimal */ + ) + 1; /* turn floor into ceil */ + else +# endif + if (type == TYPE_LONGINT || type == TYPE_ULONGINT) + tmp_length = (unsigned int) (sizeof (unsigned long) * CHAR_BIT * 0.30103 /* binary -> decimal */ + ) + 1; /* turn floor into ceil */ + else + tmp_length = (unsigned int) (sizeof (unsigned int) * CHAR_BIT * 0.30103 /* binary -> decimal */ + ) + 1; /* turn floor into ceil */ + if (tmp_length < precision) + tmp_length = precision; + /* Multiply by 2, as an estimate for FLAG_GROUP. */ + tmp_length = xsum (tmp_length, tmp_length); + /* Add 1, to account for a leading sign. */ + tmp_length = xsum (tmp_length, 1); + break; + + case 'o': +# if HAVE_LONG_LONG_INT + if (type == TYPE_LONGLONGINT + || type == TYPE_ULONGLONGINT) + tmp_length = (unsigned int) (sizeof (unsigned long long) * CHAR_BIT * 0.333334 /* binary -> octal */ + ) + 1; /* turn floor into ceil */ + else +# endif + if (type == TYPE_LONGINT || type == TYPE_ULONGINT) + tmp_length = (unsigned int) (sizeof (unsigned long) * CHAR_BIT * 0.333334 /* binary -> octal */ + ) + 1; /* turn floor into ceil */ + else + tmp_length = (unsigned int) (sizeof (unsigned int) * CHAR_BIT * 0.333334 /* binary -> octal */ + ) + 1; /* turn floor into ceil */ + if (tmp_length < precision) + tmp_length = precision; + /* Add 1, to account for a leading sign. */ + tmp_length = xsum (tmp_length, 1); + break; + + case 'x': + case 'X': +# if HAVE_LONG_LONG_INT + if (type == TYPE_LONGLONGINT + || type == TYPE_ULONGLONGINT) + tmp_length = (unsigned int) (sizeof (unsigned long long) * CHAR_BIT * 0.25 /* binary -> hexadecimal */ + ) + 1; /* turn floor into ceil */ + else +# endif + if (type == TYPE_LONGINT || type == TYPE_ULONGINT) + tmp_length = (unsigned int) (sizeof (unsigned long) * CHAR_BIT * 0.25 /* binary -> hexadecimal */ + ) + 1; /* turn floor into ceil */ + else + tmp_length = (unsigned int) (sizeof (unsigned int) * CHAR_BIT * 0.25 /* binary -> hexadecimal */ + ) + 1; /* turn floor into ceil */ + if (tmp_length < precision) + tmp_length = precision; + /* Add 2, to account for a leading sign or alternate form. */ + tmp_length = xsum (tmp_length, 2); + break; + + case 'f': + case 'F': + if (type == TYPE_LONGDOUBLE) + tmp_length = (unsigned int) (LDBL_MAX_EXP * 0.30103 /* binary -> decimal */ + * 2 /* estimate for FLAG_GROUP */ + ) + 1 /* turn floor into ceil */ + + 10; /* sign, decimal point etc. */ + else + tmp_length = (unsigned int) (DBL_MAX_EXP * 0.30103 /* binary -> decimal */ + * 2 /* estimate for FLAG_GROUP */ + ) + 1 /* turn floor into ceil */ + + 10; /* sign, decimal point etc. */ + tmp_length = xsum (tmp_length, precision); + break; + + case 'e': + case 'E': + case 'g': + case 'G': + tmp_length = 12; /* sign, decimal point, exponent etc. */ + tmp_length = xsum (tmp_length, precision); + break; + + case 'a': + case 'A': + if (type == TYPE_LONGDOUBLE) + tmp_length = (unsigned int) (LDBL_DIG * 0.831 /* decimal -> hexadecimal */ + ) + 1; /* turn floor into ceil */ + else + tmp_length = (unsigned int) (DBL_DIG * 0.831 /* decimal -> hexadecimal */ + ) + 1; /* turn floor into ceil */ + if (tmp_length < precision) + tmp_length = precision; + /* Account for sign, decimal point etc. */ + tmp_length = xsum (tmp_length, 12); + break; + + case 'c': +# if HAVE_WINT_T && !WIDE_CHAR_VERSION + if (type == TYPE_WIDE_CHAR) + tmp_length = MB_CUR_MAX; + else +# endif + tmp_length = 1; + break; + + case 's': +# if HAVE_WCHAR_T + if (type == TYPE_WIDE_STRING) + { + tmp_length = + local_wcslen (a.arg[dp->arg_index].a. + a_wide_string); + +# if !WIDE_CHAR_VERSION + tmp_length = xtimes (tmp_length, MB_CUR_MAX); +# endif + } + else +# endif + tmp_length = strlen (a.arg[dp->arg_index].a.a_string); + break; + + case 'p': + tmp_length = (unsigned int) (sizeof (void *) * CHAR_BIT * 0.25 /* binary -> hexadecimal */ + ) + 1 /* turn floor into ceil */ + + 2; /* account for leading 0x */ + break; + + default: + abort (); + } + +# if ENABLE_UNISTDIO + /* Padding considers the number of characters, therefore the + number of elements after padding may be + > max (tmp_length, width) + but is certainly + <= tmp_length + width. */ + tmp_length = xsum (tmp_length, width); +# else + /* Padding considers the number of elements, says POSIX. */ + if (tmp_length < width) + tmp_length = width; +# endif + + tmp_length = xsum (tmp_length, 1); /* account for trailing NUL */ + } + + if (tmp_length <= sizeof (tmpbuf) / sizeof (TCHAR_T)) + tmp = tmpbuf; + else + { + size_t tmp_memsize = + xtimes (tmp_length, sizeof (TCHAR_T)); + + if (size_overflow_p (tmp_memsize)) + /* Overflow, would lead to out of memory. */ + goto out_of_memory; + tmp = (TCHAR_T *) malloc (tmp_memsize); + if (tmp == NULL) + /* Out of memory. */ + goto out_of_memory; + } +#endif + + /* Decide whether to handle the precision ourselves. */ +#if NEED_PRINTF_UNBOUNDED_PRECISION + switch (dp->conversion) + { + case 'd': + case 'i': + case 'u': + case 'o': + case 'x': + case 'X': + case 'p': + prec_ourselves = has_precision && (precision > 0); + break; + default: + prec_ourselves = 0; + break; + } +#endif + + /* Decide whether to perform the padding ourselves. */ +#if !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION + switch (dp->conversion) + { +# if !DCHAR_IS_TCHAR || ENABLE_UNISTDIO + /* If we need conversion from TCHAR_T[] to DCHAR_T[], we need + to perform the padding after this conversion. Functions + with unistdio extensions perform the padding based on + character count rather than element count. */ + case 'c': + case 's': +# endif +# if NEED_PRINTF_FLAG_ZERO + case 'f': + case 'F': + case 'e': + case 'E': + case 'g': + case 'G': + case 'a': + case 'A': +# endif + pad_ourselves = 1; + break; + default: + pad_ourselves = prec_ourselves; + break; + } +#endif + + /* Construct the format string for calling snprintf or + sprintf. */ + fbp = buf; + *fbp++ = '%'; +#if NEED_PRINTF_FLAG_GROUPING + /* The underlying implementation doesn't support the ' flag. + Produce no grouping characters in this case; this is + acceptable because the grouping is locale dependent. */ +#else + if (flags & FLAG_GROUP) + *fbp++ = '\''; +#endif + if (flags & FLAG_LEFT) + *fbp++ = '-'; + if (flags & FLAG_SHOWSIGN) + *fbp++ = '+'; + if (flags & FLAG_SPACE) + *fbp++ = ' '; + if (flags & FLAG_ALT) + *fbp++ = '#'; + if (!pad_ourselves) + { + if (flags & FLAG_ZERO) + *fbp++ = '0'; + if (dp->width_start != dp->width_end) + { + size_t n = dp->width_end - dp->width_start; + /* The width specification is known to consist only + of standard ASCII characters. */ + if (sizeof (FCHAR_T) == sizeof (TCHAR_T)) + { + memcpy (fbp, dp->width_start, + n * sizeof (TCHAR_T)); + fbp += n; + } + else + { + const FCHAR_T *mp = dp->width_start; + do + *fbp++ = (unsigned char) *mp++; + while (--n > 0); + } + } + } + if (!prec_ourselves) + { + if (dp->precision_start != dp->precision_end) + { + size_t n = dp->precision_end - dp->precision_start; + /* The precision specification is known to consist only + of standard ASCII characters. */ + if (sizeof (FCHAR_T) == sizeof (TCHAR_T)) + { + memcpy (fbp, dp->precision_start, + n * sizeof (TCHAR_T)); + fbp += n; + } + else + { + const FCHAR_T *mp = dp->precision_start; + do + *fbp++ = (unsigned char) *mp++; + while (--n > 0); + } + } + } + + switch (type) + { +#if HAVE_LONG_LONG_INT + case TYPE_LONGLONGINT: + case TYPE_ULONGLONGINT: +# if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ + *fbp++ = 'I'; + *fbp++ = '6'; + *fbp++ = '4'; + break; +# else + *fbp++ = 'l'; + /*FALLTHROUGH*/ +# endif +#endif + case TYPE_LONGINT: + case TYPE_ULONGINT: +#if HAVE_WINT_T + case TYPE_WIDE_CHAR: +#endif +#if HAVE_WCHAR_T + case TYPE_WIDE_STRING: +#endif + *fbp++ = 'l'; + break; + case TYPE_LONGDOUBLE: + *fbp++ = 'L'; + break; + default: + break; + } +#if NEED_PRINTF_DIRECTIVE_F + if (dp->conversion == 'F') + *fbp = 'f'; + else +#endif + *fbp = dp->conversion; +#if USE_SNPRINTF +# if !(__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)) + fbp[1] = '%'; + fbp[2] = 'n'; + fbp[3] = '\0'; +# else + /* On glibc2 systems from glibc >= 2.3 - probably also older + ones - we know that snprintf's returns value conforms to + ISO C 99: the gl_SNPRINTF_DIRECTIVE_N test passes. + Therefore we can avoid using %n in this situation. + On glibc2 systems from 2004-10-18 or newer, the use of %n + in format strings in writable memory may crash the program + (if compiled with _FORTIFY_SOURCE=2), so we should avoid it + in this situation. */ + fbp[1] = '\0'; +# endif +#else + fbp[1] = '\0'; +#endif + + /* Construct the arguments for calling snprintf or sprintf. */ + prefix_count = 0; + if (!pad_ourselves && dp->width_arg_index != ARG_NONE) + { + if (!(a.arg[dp->width_arg_index].type == TYPE_INT)) + abort (); + prefixes[prefix_count++] = + a.arg[dp->width_arg_index].a.a_int; + } + if (dp->precision_arg_index != ARG_NONE) + { + if (!(a.arg[dp->precision_arg_index].type == TYPE_INT)) + abort (); + prefixes[prefix_count++] = + a.arg[dp->precision_arg_index].a.a_int; + } + +#if USE_SNPRINTF + /* The SNPRINTF result is appended after result[0..length]. + The latter is an array of DCHAR_T; SNPRINTF appends an + array of TCHAR_T to it. This is possible because + sizeof (TCHAR_T) divides sizeof (DCHAR_T) and + alignof (TCHAR_T) <= alignof (DCHAR_T). */ +# define TCHARS_PER_DCHAR (sizeof (DCHAR_T) / sizeof (TCHAR_T)) + /* Ensure that maxlen below will be >= 2. Needed on BeOS, + where an snprintf() with maxlen==1 acts like sprintf(). */ + ENSURE_ALLOCATION (xsum (length, + (2 + TCHARS_PER_DCHAR - 1) + / TCHARS_PER_DCHAR)); + /* Prepare checking whether snprintf returns the count + via %n. */ + *(TCHAR_T *) (result + length) = '\0'; +#endif + + for (;;) + { + int count = -1; + +#if USE_SNPRINTF + int retcount = 0; + size_t maxlen = allocated - length; + /* SNPRINTF can fail if its second argument is + > INT_MAX. */ + if (maxlen > INT_MAX / TCHARS_PER_DCHAR) + maxlen = INT_MAX / TCHARS_PER_DCHAR; + maxlen = maxlen * TCHARS_PER_DCHAR; +# define SNPRINTF_BUF(arg) \ + switch (prefix_count) \ + { \ + case 0: \ + retcount = SNPRINTF ((TCHAR_T *) (result + length), \ + maxlen, buf, \ + arg, &count); \ + break; \ + case 1: \ + retcount = SNPRINTF ((TCHAR_T *) (result + length), \ + maxlen, buf, \ + prefixes[0], arg, &count); \ + break; \ + case 2: \ + retcount = SNPRINTF ((TCHAR_T *) (result + length), \ + maxlen, buf, \ + prefixes[0], prefixes[1], arg, \ + &count); \ + break; \ + default: \ + abort (); \ + } +#else +# define SNPRINTF_BUF(arg) \ + switch (prefix_count) \ + { \ + case 0: \ + count = sprintf (tmp, buf, arg); \ + break; \ + case 1: \ + count = sprintf (tmp, buf, prefixes[0], arg); \ + break; \ + case 2: \ + count = sprintf (tmp, buf, prefixes[0], prefixes[1],\ + arg); \ + break; \ + default: \ + abort (); \ + } +#endif + + switch (type) + { + case TYPE_SCHAR: + { + int arg = a.arg[dp->arg_index].a.a_schar; + SNPRINTF_BUF (arg); + } + break; + case TYPE_UCHAR: + { + unsigned int arg = a.arg[dp->arg_index].a.a_uchar; + SNPRINTF_BUF (arg); + } + break; + case TYPE_SHORT: + { + int arg = a.arg[dp->arg_index].a.a_short; + SNPRINTF_BUF (arg); + } + break; + case TYPE_USHORT: + { + unsigned int arg = a.arg[dp->arg_index].a.a_ushort; + SNPRINTF_BUF (arg); + } + break; + case TYPE_INT: + { + int arg = a.arg[dp->arg_index].a.a_int; + SNPRINTF_BUF (arg); + } + break; + case TYPE_UINT: + { + unsigned int arg = a.arg[dp->arg_index].a.a_uint; + SNPRINTF_BUF (arg); + } + break; + case TYPE_LONGINT: + { + long int arg = a.arg[dp->arg_index].a.a_longint; + SNPRINTF_BUF (arg); + } + break; + case TYPE_ULONGINT: + { + unsigned long int arg = + a.arg[dp->arg_index].a.a_ulongint; + SNPRINTF_BUF (arg); + } + break; +#if HAVE_LONG_LONG_INT + case TYPE_LONGLONGINT: + { + long long int arg = + a.arg[dp->arg_index].a.a_longlongint; + SNPRINTF_BUF (arg); + } + break; + case TYPE_ULONGLONGINT: + { + unsigned long long int arg = + a.arg[dp->arg_index].a.a_ulonglongint; + SNPRINTF_BUF (arg); + } + break; +#endif + case TYPE_DOUBLE: + { + double arg = a.arg[dp->arg_index].a.a_double; + SNPRINTF_BUF (arg); + } + break; + case TYPE_LONGDOUBLE: + { + long double arg = + a.arg[dp->arg_index].a.a_longdouble; + SNPRINTF_BUF (arg); + } + break; + case TYPE_CHAR: + { + int arg = a.arg[dp->arg_index].a.a_char; + SNPRINTF_BUF (arg); + } + break; +#if HAVE_WINT_T + case TYPE_WIDE_CHAR: + { + wint_t arg = a.arg[dp->arg_index].a.a_wide_char; + SNPRINTF_BUF (arg); + } + break; +#endif + case TYPE_STRING: + { + const char *arg = a.arg[dp->arg_index].a.a_string; + SNPRINTF_BUF (arg); + } + break; +#if HAVE_WCHAR_T + case TYPE_WIDE_STRING: + { + const wchar_t *arg = + a.arg[dp->arg_index].a.a_wide_string; + SNPRINTF_BUF (arg); + } + break; +#endif + case TYPE_POINTER: + { + void *arg = a.arg[dp->arg_index].a.a_pointer; + SNPRINTF_BUF (arg); + } + break; + default: + abort (); + } + +#if USE_SNPRINTF + /* Portability: Not all implementations of snprintf() + are ISO C 99 compliant. Determine the number of + bytes that snprintf() has produced or would have + produced. */ + if (count >= 0) + { + /* Verify that snprintf() has NUL-terminated its + result. */ + if (count < maxlen + && ((TCHAR_T *) (result + length))[count] != '\0') + abort (); + /* Portability hack. */ + if (retcount > count) + count = retcount; + } + else + { + /* snprintf() doesn't understand the '%n' + directive. */ + if (fbp[1] != '\0') + { + /* Don't use the '%n' directive; instead, look + at the snprintf() return value. */ + fbp[1] = '\0'; + continue; + } + else + { + /* Look at the snprintf() return value. */ + if (retcount < 0) + { + /* HP-UX 10.20 snprintf() is doubly deficient: + It doesn't understand the '%n' directive, + *and* it returns -1 (rather than the length + that would have been required) when the + buffer is too small. */ + size_t bigger_need = + xsum (xtimes (allocated, 2), 12); + ENSURE_ALLOCATION (bigger_need); + continue; + } + else + count = retcount; + } + } +#endif + + /* Attempt to handle failure. */ + if (count < 0) + { + if (!(result == resultbuf || result == NULL)) + free (result); + if (buf_malloced != NULL) + free (buf_malloced); + CLEANUP (); + errno = EINVAL; + return NULL; + } + +#if USE_SNPRINTF + /* Handle overflow of the allocated buffer. + If such an overflow occurs, a C99 compliant snprintf() + returns a count >= maxlen. However, a non-compliant + snprintf() function returns only count = maxlen - 1. To + cover both cases, test whether count >= maxlen - 1. */ + if ((unsigned int) count + 1 >= maxlen) + { + /* If maxlen already has attained its allowed maximum, + allocating more memory will not increase maxlen. + Instead of looping, bail out. */ + if (maxlen == INT_MAX / TCHARS_PER_DCHAR) + goto overflow; + else + { + /* Need at least (count + 1) * sizeof (TCHAR_T) + bytes. (The +1 is for the trailing NUL.) + But ask for (count + 2) * sizeof (TCHAR_T) + bytes, so that in the next round, we likely get + maxlen > (unsigned int) count + 1 + and so we don't get here again. + And allocate proportionally, to avoid looping + eternally if snprintf() reports a too small + count. */ + size_t n = xmax (xsum (length, + ((unsigned int) count + 2 + + TCHARS_PER_DCHAR - 1) + / TCHARS_PER_DCHAR), + xtimes (allocated, 2)); + + ENSURE_ALLOCATION (n); + continue; + } + } +#endif + +#if NEED_PRINTF_UNBOUNDED_PRECISION + if (prec_ourselves) + { + /* Handle the precision. */ + TCHAR_T *prec_ptr = +# if USE_SNPRINTF + (TCHAR_T *) (result + length); +# else + tmp; +# endif + size_t prefix_count; + size_t move; + + prefix_count = 0; + /* Put the additional zeroes after the sign. */ + if (count >= 1 + && (*prec_ptr == '-' || *prec_ptr == '+' + || *prec_ptr == ' ')) + prefix_count = 1; + /* Put the additional zeroes after the 0x prefix if + (flags & FLAG_ALT) || (dp->conversion == 'p'). */ + else if (count >= 2 + && prec_ptr[0] == '0' + && (prec_ptr[1] == 'x' + || prec_ptr[1] == 'X')) + prefix_count = 2; + + move = count - prefix_count; + if (precision > move) + { + /* Insert zeroes. */ + size_t insert = precision - move; + TCHAR_T *prec_end; + +# if USE_SNPRINTF + size_t n = xsum (length, + (count + insert + + TCHARS_PER_DCHAR - + 1) / TCHARS_PER_DCHAR); + length += + (count + TCHARS_PER_DCHAR - + 1) / TCHARS_PER_DCHAR; + ENSURE_ALLOCATION (n); + length -= + (count + TCHARS_PER_DCHAR - + 1) / TCHARS_PER_DCHAR; + prec_ptr = (TCHAR_T *) (result + length); +# endif + + prec_end = prec_ptr + count; + prec_ptr += prefix_count; + + while (prec_end > prec_ptr) + { + prec_end--; + prec_end[insert] = prec_end[0]; + } + + prec_end += insert; + do + *--prec_end = '0'; + while (prec_end > prec_ptr); + + count += insert; + } + } +#endif + +#if !DCHAR_IS_TCHAR +# if !USE_SNPRINTF + if (count >= tmp_length) + /* tmp_length was incorrectly calculated - fix the + code above! */ + abort (); +# endif + + /* Convert from TCHAR_T[] to DCHAR_T[]. */ + if (dp->conversion == 'c' || dp->conversion == 's') + { + /* type = TYPE_CHAR or TYPE_WIDE_CHAR or TYPE_STRING + TYPE_WIDE_STRING. + The result string is not certainly ASCII. */ + const TCHAR_T *tmpsrc; + DCHAR_T *tmpdst; + size_t tmpdst_len; + /* This code assumes that TCHAR_T is 'char'. */ + typedef int TCHAR_T_verify + [2 * (sizeof (TCHAR_T) == 1) - 1]; +# if USE_SNPRINTF + tmpsrc = (TCHAR_T *) (result + length); +# else + tmpsrc = tmp; +# endif + tmpdst = NULL; + tmpdst_len = 0; + if (DCHAR_CONV_FROM_ENCODING (locale_charset (), + iconveh_question_mark, + tmpsrc, count, + NULL, + &tmpdst, &tmpdst_len) + < 0) + { + int saved_errno = errno; + if (!(result == resultbuf || result == NULL)) + free (result); + if (buf_malloced != NULL) + free (buf_malloced); + CLEANUP (); + errno = saved_errno; + return NULL; + } + ENSURE_ALLOCATION (xsum (length, tmpdst_len)); + DCHAR_CPY (result + length, tmpdst, tmpdst_len); + free (tmpdst); + count = tmpdst_len; + } + else + { + /* The result string is ASCII. + Simple 1:1 conversion. */ +# if USE_SNPRINTF + /* If sizeof (DCHAR_T) == sizeof (TCHAR_T), it's a + no-op conversion, in-place on the array starting + at (result + length). */ + if (sizeof (DCHAR_T) != sizeof (TCHAR_T)) +# endif + { + const TCHAR_T *tmpsrc; + DCHAR_T *tmpdst; + size_t n; + +# if USE_SNPRINTF + if (result == resultbuf) + { + tmpsrc = (TCHAR_T *) (result + length); + /* ENSURE_ALLOCATION will not move tmpsrc + (because it's part of resultbuf). */ + ENSURE_ALLOCATION (xsum (length, count)); + } + else + { + /* ENSURE_ALLOCATION will move the array + (because it uses realloc(). */ + ENSURE_ALLOCATION (xsum (length, count)); + tmpsrc = (TCHAR_T *) (result + length); + } +# else + tmpsrc = tmp; + ENSURE_ALLOCATION (xsum (length, count)); +# endif + tmpdst = result + length; + /* Copy backwards, because of overlapping. */ + tmpsrc += count; + tmpdst += count; + for (n = count; n > 0; n--) + *--tmpdst = (unsigned char) *--tmpsrc; + } + } +#endif + +#if DCHAR_IS_TCHAR && !USE_SNPRINTF + /* Make room for the result. */ + if (count > allocated - length) + { + /* Need at least count elements. But allocate + proportionally. */ + size_t n = + xmax (xsum (length, count), xtimes (allocated, 2)); + + ENSURE_ALLOCATION (n); + } +#endif + + /* Here count <= allocated - length. */ + + /* Perform padding. */ +#if !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION + if (pad_ourselves && has_width) + { + size_t w; +# if ENABLE_UNISTDIO + /* Outside POSIX, it's preferrable to compare the width + against the number of _characters_ of the converted + value. */ + w = DCHAR_MBSNLEN (result + length, count); +# else + /* The width is compared against the number of _bytes_ + of the converted value, says POSIX. */ + w = count; +# endif + if (w < width) + { + size_t pad = width - w; +# if USE_SNPRINTF + /* Make room for the result. */ + if (xsum (count, pad) > allocated - length) + { + /* Need at least count + pad elements. But + allocate proportionally. */ + size_t n = xmax (xsum3 (length, count, pad), + xtimes (allocated, 2)); + + length += count; + ENSURE_ALLOCATION (n); + length -= count; + } + /* Here count + pad <= allocated - length. */ +# endif + { +# if !DCHAR_IS_TCHAR || USE_SNPRINTF + DCHAR_T *const rp = result + length; +# else + DCHAR_T *const rp = tmp; +# endif + DCHAR_T *p = rp + count; + DCHAR_T *end = p + pad; +# if NEED_PRINTF_FLAG_ZERO + DCHAR_T *pad_ptr; +# if !DCHAR_IS_TCHAR + if (dp->conversion == 'c' + || dp->conversion == 's') + /* No zero-padding for string directives. */ + pad_ptr = NULL; + else +# endif + { + pad_ptr = (*rp == '-' ? rp + 1 : rp); + /* No zero-padding of "inf" and "nan". */ + if ((*pad_ptr >= 'A' && *pad_ptr <= 'Z') + || (*pad_ptr >= 'a' && *pad_ptr <= 'z')) + pad_ptr = NULL; + } +# endif + /* The generated string now extends from rp to p, + with the zero padding insertion point being at + pad_ptr. */ + + count = count + pad; /* = end - rp */ + + if (flags & FLAG_LEFT) + { + /* Pad with spaces on the right. */ + for (; pad > 0; pad--) + *p++ = ' '; + } +# if NEED_PRINTF_FLAG_ZERO + else if ((flags & FLAG_ZERO) && pad_ptr != NULL) + { + /* Pad with zeroes. */ + DCHAR_T *q = end; + + while (p > pad_ptr) + *--q = *--p; + for (; pad > 0; pad--) + *p++ = '0'; + } +# endif + else + { + /* Pad with spaces on the left. */ + DCHAR_T *q = end; + + while (p > rp) + *--q = *--p; + for (; pad > 0; pad--) + *p++ = ' '; + } + } + } + } +#endif + +#if DCHAR_IS_TCHAR && !USE_SNPRINTF + if (count >= tmp_length) + /* tmp_length was incorrectly calculated - fix the + code above! */ + abort (); +#endif + + /* Here still count <= allocated - length. */ + +#if !DCHAR_IS_TCHAR || USE_SNPRINTF + /* The snprintf() result did fit. */ +#else + /* Append the sprintf() result. */ + memcpy (result + length, tmp, count * sizeof (DCHAR_T)); +#endif +#if !USE_SNPRINTF + if (tmp != tmpbuf) + free (tmp); +#endif + +#if NEED_PRINTF_DIRECTIVE_F + if (dp->conversion == 'F') + { + /* Convert the %f result to upper case for %F. */ + DCHAR_T *rp = result + length; + size_t rc; + for (rc = count; rc > 0; rc--, rp++) + if (*rp >= 'a' && *rp <= 'z') + *rp = *rp - 'a' + 'A'; + } +#endif + + length += count; + break; + } + } + } + } + + /* Add the final NUL. */ + ENSURE_ALLOCATION (xsum (length, 1)); + result[length] = '\0'; + + if (result != resultbuf && length + 1 < allocated) + { + /* Shrink the allocated memory if possible. */ + DCHAR_T *memory; + + memory = + (DCHAR_T *) realloc (result, (length + 1) * sizeof (DCHAR_T)); + if (memory != NULL) + result = memory; + } + + if (buf_malloced != NULL) + free (buf_malloced); + CLEANUP (); + *lengthp = length; + /* Note that we can produce a big string of a length > INT_MAX. POSIX + says that snprintf() fails with errno = EOVERFLOW in this case, but + that's only because snprintf() returns an 'int'. This function does + not have this limitation. */ + return result; + + overflow: + if (!(result == resultbuf || result == NULL)) + free (result); + if (buf_malloced != NULL) + free (buf_malloced); + CLEANUP (); + errno = EOVERFLOW; + return NULL; + + out_of_memory: + if (!(result == resultbuf || result == NULL)) + free (result); + if (buf_malloced != NULL) + free (buf_malloced); + out_of_memory_1: + CLEANUP (); + errno = ENOMEM; + return NULL; + } +} + +#undef TCHARS_PER_DCHAR +#undef SNPRINTF +#undef USE_SNPRINTF +#undef DCHAR_CPY +#undef PRINTF_PARSE +#undef DIRECTIVES +#undef DIRECTIVE +#undef DCHAR_IS_TCHAR +#undef TCHAR_T +#undef DCHAR_T +#undef FCHAR_T +#undef VASNPRINTF diff --git a/src/daemon/https/lgl/vasnprintf.h b/src/daemon/https/lgl/vasnprintf.h new file mode 100644 index 00000000..4524ce77 --- /dev/null +++ b/src/daemon/https/lgl/vasnprintf.h @@ -0,0 +1,81 @@ +/* vsprintf with automatic memory allocation. + Copyright (C) 2002-2004, 2007 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1, or (at your option) + any later version. + + This program 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef _VASNPRINTF_H +#define _VASNPRINTF_H + +/* Get va_list. */ +#include + +/* Get size_t. */ +#include + +#ifndef __attribute__ +/* This feature is available in gcc versions 2.5 and later. */ +# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 5) || __STRICT_ANSI__ +# define __attribute__(Spec) /* empty */ +# endif +/* The __-protected variants of `format' and `printf' attributes + are accepted by gcc versions 2.6.4 (effectively 2.7) and later. */ +# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 7) +# define __format__ format +# define __printf__ printf +# endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Write formatted output to a string dynamically allocated with malloc(). + You can pass a preallocated buffer for the result in RESULTBUF and its + size in *LENGTHP; otherwise you pass RESULTBUF = NULL. + If successful, return the address of the string (this may be = RESULTBUF + if no dynamic memory allocation was necessary) and set *LENGTHP to the + number of resulting bytes, excluding the trailing NUL. Upon error, set + errno and return NULL. + + When dynamic memory allocation occurs, the preallocated buffer is left + alone (with possibly modified contents). This makes it possible to use + a statically allocated or stack-allocated buffer, like this: + + char buf[100]; + size_t len = sizeof (buf); + char *output = vasnprintf (buf, &len, format, args); + if (output == NULL) + ... error handling ...; + else + { + ... use the output string ...; + if (output != buf) + free (output); + } + */ +#if REPLACE_VASNPRINTF +# define asnprintf rpl_asnprintf +# define vasnprintf rpl_vasnprintf +#endif +extern char * asnprintf (char *resultbuf, size_t *lengthp, const char *format, ...) + __attribute__ ((__format__ (__printf__, 3, 4))); +extern char * vasnprintf (char *resultbuf, size_t *lengthp, const char *format, va_list args) + __attribute__ ((__format__ (__printf__, 3, 0))); + +#ifdef __cplusplus +} +#endif + +#endif /* _VASNPRINTF_H */ diff --git a/src/daemon/https/lgl/vasprintf.c b/src/daemon/https/lgl/vasprintf.c new file mode 100644 index 00000000..7b645460 --- /dev/null +++ b/src/daemon/https/lgl/vasprintf.c @@ -0,0 +1,56 @@ +/* Formatted output to strings. + Copyright (C) 1999, 2002, 2006-2007 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1, or (at your option) + any later version. + + This program 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#include + +/* Specification. */ +#ifdef IN_LIBASPRINTF +# include "vasprintf.h" +#else +# include +#endif + +#include +#include +#include + +#include "vasnprintf.h" + +/* Some systems, like OSF/1 4.0 and Woe32, don't have EOVERFLOW. */ +#ifndef EOVERFLOW +# define EOVERFLOW E2BIG +#endif + +int +vasprintf (char **resultp, const char *format, va_list args) +{ + size_t length; + char *result = vasnprintf (NULL, &length, format, args); + if (result == NULL) + return -1; + + if (length > INT_MAX) + { + free (result); + errno = EOVERFLOW; + return -1; + } + + *resultp = result; + /* Return the number of resulting bytes, excluding the trailing NUL. */ + return length; +} diff --git a/src/daemon/https/lgl/xsize.h b/src/daemon/https/lgl/xsize.h new file mode 100644 index 00000000..d37de38a --- /dev/null +++ b/src/daemon/https/lgl/xsize.h @@ -0,0 +1,109 @@ +/* xsize.h -- Checked size_t computations. + + Copyright (C) 2003 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1, or (at your option) + any later version. + + This program 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef _XSIZE_H +#define _XSIZE_H + +/* Get size_t. */ +#include + +/* Get SIZE_MAX. */ +#include +#if HAVE_STDINT_H +# include +#endif + +/* The size of memory objects is often computed through expressions of + type size_t. Example: + void* p = malloc (header_size + n * element_size). + These computations can lead to overflow. When this happens, malloc() + returns a piece of memory that is way too small, and the program then + crashes while attempting to fill the memory. + To avoid this, the functions and macros in this file check for overflow. + The convention is that SIZE_MAX represents overflow. + malloc (SIZE_MAX) is not guaranteed to fail -- think of a malloc + implementation that uses mmap --, it's recommended to use size_overflow_p() + or size_in_bounds_p() before invoking malloc(). + The example thus becomes: + size_t size = xsum (header_size, xtimes (n, element_size)); + void *p = (size_in_bounds_p (size) ? malloc (size) : NULL); +*/ + +/* Convert an arbitrary value >= 0 to type size_t. */ +#define xcast_size_t(N) \ + ((N) <= SIZE_MAX ? (size_t) (N) : SIZE_MAX) + +/* Sum of two sizes, with overflow check. */ +static inline size_t +#if __GNUC__ >= 3 +__attribute__ ((__pure__)) +#endif +xsum (size_t size1, size_t size2) +{ + size_t sum = size1 + size2; + return (sum >= size1 ? sum : SIZE_MAX); +} + +/* Sum of three sizes, with overflow check. */ +static inline size_t +#if __GNUC__ >= 3 +__attribute__ ((__pure__)) +#endif +xsum3 (size_t size1, size_t size2, size_t size3) +{ + return xsum (xsum (size1, size2), size3); +} + +/* Sum of four sizes, with overflow check. */ +static inline size_t +#if __GNUC__ >= 3 +__attribute__ ((__pure__)) +#endif +xsum4 (size_t size1, size_t size2, size_t size3, size_t size4) +{ + return xsum (xsum (xsum (size1, size2), size3), size4); +} + +/* Maximum of two sizes, with overflow check. */ +static inline size_t +#if __GNUC__ >= 3 +__attribute__ ((__pure__)) +#endif +xmax (size_t size1, size_t size2) +{ + /* No explicit check is needed here, because for any n: + max (SIZE_MAX, n) == SIZE_MAX and max (n, SIZE_MAX) == SIZE_MAX. */ + return (size1 >= size2 ? size1 : size2); +} + +/* Multiplication of a count with an element size, with overflow check. + The count must be >= 0 and the element size must be > 0. + This is a macro, not an inline function, so that it works correctly even + when N is of a wider tupe and N > SIZE_MAX. */ +#define xtimes(N, ELSIZE) \ + ((N) <= SIZE_MAX / (ELSIZE) ? (size_t) (N) * (ELSIZE) : SIZE_MAX) + +/* Check for overflow. */ +#define size_overflow_p(SIZE) \ + ((SIZE) == SIZE_MAX) +/* Check against overflow. */ +#define size_in_bounds_p(SIZE) \ + ((SIZE) != SIZE_MAX) + +#endif /* _XSIZE_H */ + diff --git a/src/daemon/https/list.h b/src/daemon/https/list.h new file mode 100644 index 00000000..f1c2faa1 --- /dev/null +++ b/src/daemon/https/list.h @@ -0,0 +1,449 @@ +/* + * Copyright (C) 2001,2002 Paul Sheer + * + * This file is part of GNUTLS. + * + * GNUTLS is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * GNUTLS 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/* + SOAP: + + Academics always want to implement hash tables (i.e. dictionaries), + singly or doubly linked lists, and queues, because ... well ... they + know how. + + These datatypes are nonsense for the following reasons: + hash tables: Hash tables are a mapping of some + string to some data, where that data is going to + be accessed COMPLETELY RANDOMLY. This is what it + is for. However it is extremely rare to have a + large number of data elements which really are + being accessed in a completely random way. + + lists: appending and searching through lists is always + slow because these operations search all the way + through the list. + + queues: whats the difference between a queue and a list? + very little really. + + The system implemented here is a doubly linked list with previous + search index that can be appended or prepended with no overhead, + implemented entirely in macros. It is hence as versatile as a + doubly/singly linked list or queue and blazingly fast. Hence doing + sequential searches where the next search result is likely to be + closely indexed to the previous (usual case), is efficient. + + Of course this doesn't mean you should use this as a hash table + where you REALLY need a hash table. + +*/ + +/********** example usage **********/ +/* + +#include "list.h" + +extern void free (void *x); +extern char *strdup (char *s); + +// consider a list of elements containing an `int' and a `char *' +LIST_TYPE_DECLARE (names, char *s; int i;); + +// for sorting, to compare elements +static int cm (names **a, names **b) +{ + return strcmp ((*a)->s, (*b)->s); +} + +// to free the contents of an element +static void free_item (names *a) +{ + free (a->s); + a->s = 0; // say + a->i = 0; // say +} + +int main (int argc, char **argv) +{ +// you can separate these into LIST_TYPE_DECLARE(), LIST_DECLARE() and linit() if needed. + LIST_DECLARE_INIT (l, names, free_item); + names *j; + + lappend (l); + l.tail->s = strdup ("hello"); + l.tail->i = 1; + lappend (l); + l.tail->s = strdup ("there"); + l.tail->i = 2; + lappend (l); + l.tail->s = strdup ("my"); + l.tail->i = 3; + lappend (l); + l.tail->s = strdup ("name"); + l.tail->i = 4; + lappend (l); + l.tail->s = strdup ("is"); + l.tail->i = 5; + lappend (l); + l.tail->s = strdup ("fred"); + l.tail->i = 6; + + printf ("%ld\n\n", lcount (l)); + lloopforward (l, j, printf ("%d %s\n", j->i, j->s)); + printf ("\n"); + + lsort (l, cm); + lloopforward (l, j, printf ("%d %s\n", j->i, j->s)); + + lloopreverse (l, j, if (j->i <= 3) ldeleteinc (l, j);); + + printf ("\n"); + lloopforward (l, j, printf ("%d %s\n", j->i, j->s)); + + ldeleteall (l); + + printf ("\n"); + lloopforward (l, j, printf ("%d %s\n", j->i, j->s)); + return 0; +} + +*/ + + +#ifndef _LIST_H +#define _LIST_H + +/* the `search' member points to the last found. + this speeds up repeated searches on the same list-item, + the consecutive list-item, or the pre-consecutive list-item. + this obviates the need for a hash table for 99% of + cercumstances the time */ +struct list +{ + long length; + long item_size; + struct list_item + { + struct list_item *next; + struct list_item *prev; + char data[1]; + } *head, *tail, *search; + void (*free_func) (struct list_item *); +}; + +/* declare a list of type `x', also called `x' having members `typelist' */ + +#define LIST_TYPE_DECLARE(type,typelist) \ + typedef struct type { \ + struct type *next; \ + struct type *prev; \ + typelist \ + } type + +#define LIST_DECLARE(l,type) \ + struct { \ + long length; \ + long item_size; \ + type *head, *tail, *search; \ + void (*free_func) (type *); \ + } l + +#define LIST_DECLARE_INIT(l,type,free) \ + struct { \ + long length; \ + long item_size; \ + type *head, *tail, *search; \ + void (*free_func) (type *); \ + } l = {0, sizeof (type), 0, 0, 0, (void (*) (type *)) free} + +#define linit(l,type,free) \ + { \ + memset (&(l), 0, sizeof (l)); \ + (l).item_size = sizeof (type); \ + (l).free_func = free; \ + } + +/* returns a pointer to the data of an item */ +#define ldata(i) ((void *) &((i)->data[0])) + +/* returns a pointer to the list head */ +#define lhead(l) ((l).head) + +/* returns a pointer to the list tail */ +#define ltail(l) ((l).tail) + +#define lnewsearch(l) \ + (l).search = 0; + +/* adds to the beginning of the list */ +#define lprepend(l) \ + { \ + struct list_item *__t; \ + __t = (struct list_item *) malloc ((l).item_size); \ + memset (__t, 0, (l).item_size); \ + __t->next = (l).head; \ + if (__t->next) \ + __t->next->prev = __t; \ + __t->prev = 0; \ + if (!(l).tail) \ + (l).tail = __t; \ + (l).head = __t; \ + length++; \ + } + +/* adds to the end of the list */ +#define lappend(l) \ + { \ + struct list_item *__t; \ + __t = (struct list_item *) malloc ((l).item_size); \ + memset (__t, 0, (l).item_size); \ + __t->prev = (struct list_item *) (l).tail; \ + if (__t->prev) \ + __t->prev->next = __t; \ + __t->next = 0; \ + if (!(l).head) \ + (l).head = (void *) __t; \ + (l).tail = (void *) __t; \ + (l).length++; \ + } + +/* you should free these manually */ +#define lunlink(l,e) \ + { \ + struct list_item *__t; \ + (l).search = 0; \ + __t = (void *) e; \ + if ((void *) (l).head == (void *) __t) \ + (l).head = (l).head->next; \ + if ((void *) (l).tail == (void *) __t) \ + (l).tail = (l).tail->prev; \ + if (__t->next) \ + __t->next->prev = __t->prev; \ + if (__t->prev) \ + __t->prev->next = __t->next; \ + (l).length--; \ + } + +/* deletes list entry at point e, and increments e to the following list entry */ +#define ldeleteinc(l,e) \ + { \ + struct list_item *__t; \ + (l).search = 0; \ + __t = (void *) e; \ + if ((void *) (l).head == (void *) __t) \ + (l).head = (l).head->next; \ + if ((void *) (l).tail == (void *) __t) \ + (l).tail = (l).tail->prev; \ + if (__t->next) \ + __t->next->prev = __t->prev; \ + if (__t->prev) \ + __t->prev->next = __t->next; \ + __t = __t->next; \ + if ((l).free_func) \ + (*(l).free_func) ((void *) e); \ + free (e); \ + e = (void *) __t; \ + (l).length--; \ + } + +/* deletes list entry at point e, and deccrements e to the preceding list emtry */ +#define ldeletedec(l,e) \ + { \ + struct list_item *__t; \ + (l).search = 0; \ + __t = (void *) e; \ + if ((void *) (l).head == (void *) __t) \ + (l).head = (l).head->next; \ + if ((void *) (l).tail == (void *) __t) \ + (l).tail = (l).tail->prev; \ + if (__t->next) \ + __t->next->prev = __t->prev; \ + if (__t->prev) \ + __t->prev->next = __t->next; \ + __t = __t->prev; \ + if ((l).free_func) \ + (*(l).free_func) ((void *) e); \ + free (e); \ + e = (void *) __t; \ + (l).length--; \ + } + +/* p and q must be consecutive and neither must be zero */ +#define linsert(l,p,q) \ + { \ + struct list_item *__t; \ + __t = (struct list_item *) malloc ((l).item_size); \ + memset (__t, 0, (l).item_size); \ + __t->prev = (void *) p; \ + __t->next = (void *) q; \ + q->prev = (void *) __t; \ + p->next = (void *) __t; \ + (l).length++; \ + } + +/* p and q must be consecutive and neither must be zero */ +#define ldeleteall(l) \ + { \ + (l).search = 0; \ + while ((l).head) { \ + struct list_item *__p; \ + __p = (struct list_item *) (l).head; \ + lunlink(l, __p); \ + if ((l).free_func) \ + (*(l).free_func) ((void *) __p); \ + free (__p); \ + } \ + } + +#define lloopstart(l,i) \ + for (i = (void *) (l).head; i;) { \ + struct list_item *__tl; \ + __tl = (void *) i->next; \ + +#define lloopend(l,i) \ + i = (void *) __tl; \ + } \ + +#define lloopforward(l,i,op) \ + { \ + for (i = (void *) (l).head; i;) { \ + struct list_item *__t; \ + __t = (void *) i->next; \ + op; \ + i = (void *) __t; \ + } \ + } + +#define lloopreverse(l,i,op) \ + { \ + for (i = (void *) (l).tail; i;) { \ + struct list_item *__t; \ + __t = (void *) i->prev; \ + op; \ + i = (void *) __t; \ + } \ + } + +#define lindex(l,i,n) \ + { \ + int __k; \ + for (__k = 0, i = (void *) (l).head; i && __k != n; i = i->next, __k++); \ + } + +#define lsearchforward(l,i,op) \ + { \ + int __found = 0; \ + if (!__found) \ + if ((i = (void *) (l).search)) { \ + if (op) { \ + __found = 1; \ + (l).search = (void *) i; \ + } \ + if (!__found) \ + if ((i = (void *) (l).search->next)) \ + if (op) { \ + __found = 1; \ + (l).search = (void *) i; \ + } \ + if (!__found) \ + if ((i = (void *) (l).search->prev)) \ + if (op) { \ + __found = 1; \ + (l).search = (void *) i; \ + } \ + } \ + if (!__found) \ + for (i = (void *) (l).head; i; i = i->next) \ + if (op) { \ + __found = 1; \ + (l).search = (void *) i; \ + break; \ + } \ + } + +#define lsearchreverse(l,i,op) \ + { \ + int __found = 0; \ + if (!__found) \ + if ((i = (void *) (l).search)) { \ + if (op) { \ + __found = 1; \ + (l).search = (void *) i; \ + } \ + if (!__found) \ + if ((i = (void *) (l).search->prev)) \ + if (op) { \ + __found = 1; \ + (l).search = (void *) i; \ + } \ + if (!__found) \ + if ((i = (void *) (l).search->next)) \ + if (op) { \ + __found = 1; \ + (l).search = (void *) i; \ + } \ + } \ + if (!__found) \ + for (i = (void *) (l).tail; i; i = i->prev) \ + if (op) { \ + __found = 1; \ + (l).search = (void *) i; \ + break; \ + } \ + } + +#define lcount(l) ((l).length) + +/* sort with comparison function see qsort(3) */ +#define larray(l,a) \ + { \ + long __i; \ + struct list_item *__p; \ + a = (void *) malloc (((l).length + 1) * sizeof (void *)); \ + for (__i = 0, __p = (void *) (l).head; __p; __p = __p->next, __i++) \ + a[__i] = (void *) __p; \ + a[__i] = 0; \ + } \ + +/* sort with comparison function see qsort(3) */ +#define llist(l,a) \ + { \ + struct list_item *__p; \ + (l).head = (void *) a[0]; \ + (l).search = 0; \ + __p = (void *) a[0]; \ + __p->prev = 0; \ + for (__j = 1; a[__j]; __j++, __p = __p->next) { \ + __p->next = (void *) a[__j]; \ + __p->next->prev = __p; \ + } \ + (l).tail = (void *) __p; \ + __p->next = 0; \ + } \ + +/* sort with comparison function see qsort(3) */ +#define lsort(l,compare) \ + { \ + void **__t; \ + long __j; \ + larray (l,__t); \ + qsort (__t, (l).length, sizeof (void *), (int (*) (const void *, const void *)) compare); \ + llist (l,__t); \ + free (__t); \ + } \ + +#endif /* _LIST_H */ diff --git a/src/daemon/https/minitasn1/Makefile.am b/src/daemon/https/minitasn1/Makefile.am new file mode 100644 index 00000000..fc924cc4 --- /dev/null +++ b/src/daemon/https/minitasn1/Makefile.am @@ -0,0 +1,16 @@ + +AM_CPPFLAGS = -I./includes \ +-I$(top_srcdir)/src/daemon/https/lgl \ +-I$(top_srcdir)/src/daemon/https/tls \ +-I$(top_srcdir)/src/daemon/https/includes + +noinst_LTLIBRARIES = libasn1.la + +libasn1_la_SOURCES = \ +libtasn1.h \ +mem.h \ +gstr.h \ +errors.h \ +int.h \ +parser_aux.h structure.h element.h decoding.c gstr.c errors.c \ +parser_aux.c structure.c element.c coding.c diff --git a/src/daemon/https/minitasn1/README b/src/daemon/https/minitasn1/README new file mode 100644 index 00000000..9d484dfc --- /dev/null +++ b/src/daemon/https/minitasn1/README @@ -0,0 +1,3 @@ +This is just a mirror of the files in the libtasn1's +lib/ directory. + diff --git a/src/daemon/https/minitasn1/coding.c b/src/daemon/https/minitasn1/coding.c new file mode 100644 index 00000000..10870e01 --- /dev/null +++ b/src/daemon/https/minitasn1/coding.c @@ -0,0 +1,1229 @@ +/* + * Copyright (C) 2004, 2006 Free Software Foundation + * Copyright (C) 2002 Fabio Fiorina + * + * This file is part of LIBTASN1. + * + * The LIBTASN1 library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +/*****************************************************/ +/* File: coding.c */ +/* Description: Functions to create a DER coding of */ +/* an ASN1 type. */ +/*****************************************************/ + +#include +#include +#include "parser_aux.h" +#include +#include "element.h" +#include + +#define MAX_TAG_LEN 16 + +/******************************************************/ +/* Function : _asn1_error_description_value_not_found */ +/* Description: creates the ErrorDescription string */ +/* for the ASN1_VALUE_NOT_FOUND error. */ +/* Parameters: */ +/* node: node of the tree where the value is NULL. */ +/* ErrorDescription: string returned. */ +/* Return: */ +/******************************************************/ +void +_asn1_error_description_value_not_found (node_asn * node, + char *ErrorDescription) +{ + + if (ErrorDescription == NULL) + return; + + Estrcpy (ErrorDescription, ":: value of element '"); + _asn1_hierarchical_name (node, ErrorDescription + strlen (ErrorDescription), + MAX_ERROR_DESCRIPTION_SIZE - 40); + Estrcat (ErrorDescription, "' not found"); + +} + +/** + * asn1_length_der: + * @len: value to convert. + * @ans: string returned. + * @ans_len: number of meaningful bytes of ANS (ans[0]..ans[ans_len-1]). + * + * Creates the DER coding for the LEN parameter (only the length). + * The @ans buffer is pre-allocated and must have room for the output. + **/ +void +asn1_length_der (unsigned long int len, unsigned char *ans, int *ans_len) +{ + int k; + unsigned char temp[SIZEOF_UNSIGNED_LONG_INT]; + + if (len < 128) + { + /* short form */ + if (ans != NULL) + ans[0] = (unsigned char) len; + *ans_len = 1; + } + else + { + /* Long form */ + k = 0; + while (len) + { + temp[k++] = len & 0xFF; + len = len >> 8; + } + *ans_len = k + 1; + if (ans != NULL) + { + ans[0] = ((unsigned char) k & 0x7F) + 128; + while (k--) + ans[*ans_len - 1 - k] = temp[k]; + } + } +} + +/******************************************************/ +/* Function : _asn1_tag_der */ +/* Description: creates the DER coding for the CLASS */ +/* and TAG parameters. */ +/* Parameters: */ +/* class: value to convert. */ +/* tag_value: value to convert. */ +/* ans: string returned. */ +/* ans_len: number of meaningful bytes of ANS */ +/* (ans[0]..ans[ans_len-1]). */ +/* Return: */ +/******************************************************/ +void +_asn1_tag_der (unsigned char class, unsigned int tag_value, + unsigned char *ans, int *ans_len) +{ + int k; + unsigned char temp[SIZEOF_UNSIGNED_INT]; + + if (tag_value < 31) + { + /* short form */ + ans[0] = (class & 0xE0) + ((unsigned char) (tag_value & 0x1F)); + *ans_len = 1; + } + else + { + /* Long form */ + ans[0] = (class & 0xE0) + 31; + k = 0; + while (tag_value) + { + temp[k++] = tag_value & 0x7F; + tag_value = tag_value >> 7; + } + *ans_len = k + 1; + while (k--) + ans[*ans_len - 1 - k] = temp[k] + 128; + ans[*ans_len - 1] -= 128; + } +} + +/** + * asn1_octet_der: + * @str: OCTET string. + * @str_len: STR length (str[0]..str[str_len-1]). + * @der: string returned. + * @der_len: number of meaningful bytes of DER (der[0]..der[ans_len-1]). + * + * Creates the DER coding for an OCTET type (length included). + **/ +void +asn1_octet_der (const unsigned char *str, int str_len, + unsigned char *der, int *der_len) +{ + int len_len; + + if (der == NULL || str_len < 0) + return; + asn1_length_der (str_len, der, &len_len); + memcpy (der + len_len, str, str_len); + *der_len = str_len + len_len; +} + +/******************************************************/ +/* Function : _asn1_time_der */ +/* Description: creates the DER coding for a TIME */ +/* type (length included). */ +/* Parameters: */ +/* str: TIME null-terminated string. */ +/* der: string returned. */ +/* der_len: number of meaningful bytes of DER */ +/* (der[0]..der[ans_len-1]). Initially it */ +/* if must store the lenght of DER. */ +/* Return: */ +/* ASN1_MEM_ERROR when DER isn't big enough */ +/* ASN1_SUCCESS otherwise */ +/******************************************************/ +asn1_retCode +_asn1_time_der (unsigned char *str, unsigned char *der, int *der_len) +{ + int len_len; + int max_len; + + max_len = *der_len; + + asn1_length_der (strlen (str), (max_len > 0) ? der : NULL, &len_len); + + if ((len_len + (int) strlen (str)) <= max_len) + memcpy (der + len_len, str, strlen (str)); + *der_len = len_len + strlen (str); + + if ((*der_len) > max_len) + return ASN1_MEM_ERROR; + + return ASN1_SUCCESS; +} + + +/* +void +_asn1_get_utctime_der(unsigned char *der,int *der_len,unsigned char *str) +{ + int len_len,str_len; + char temp[20]; + + if(str==NULL) return; + str_len=asn1_get_length_der(der,*der_len,&len_len); + if (str_len<0) return; + memcpy(temp,der+len_len,str_len); + *der_len=str_len+len_len; + switch(str_len){ + case 11: + temp[10]=0; + strcat(temp,"00+0000"); + break; + case 13: + temp[12]=0; + strcat(temp,"+0000"); + break; + case 15: + temp[15]=0; + memmove(temp+12,temp+10,6); + temp[10]=temp[11]='0'; + break; + case 17: + temp[17]=0; + break; + default: + return; + } + strcpy(str,temp); +} +*/ + +/******************************************************/ +/* Function : _asn1_objectid_der */ +/* Description: creates the DER coding for an */ +/* OBJECT IDENTIFIER type (length included). */ +/* Parameters: */ +/* str: OBJECT IDENTIFIER null-terminated string. */ +/* der: string returned. */ +/* der_len: number of meaningful bytes of DER */ +/* (der[0]..der[ans_len-1]). Initially it */ +/* must store the length of DER. */ +/* Return: */ +/* ASN1_MEM_ERROR when DER isn't big enough */ +/* ASN1_SUCCESS otherwise */ +/******************************************************/ +asn1_retCode +_asn1_objectid_der (unsigned char *str, unsigned char *der, int *der_len) +{ + int len_len, counter, k, first, max_len; + char *temp, *n_end, *n_start; + unsigned char bit7; + unsigned long val, val1 = 0; + + max_len = *der_len; + + temp = (char *) _asn1_alloca (strlen (str) + 2); + if (temp == NULL) + return ASN1_MEM_ALLOC_ERROR; + + strcpy (temp, str); + strcat (temp, "."); + + counter = 0; + n_start = temp; + while ((n_end = strchr (n_start, '.'))) + { + *n_end = 0; + val = strtoul (n_start, NULL, 10); + counter++; + + if (counter == 1) + val1 = val; + else if (counter == 2) + { + if (max_len > 0) + der[0] = 40 * val1 + val; + *der_len = 1; + } + else + { + first = 0; + for (k = 4; k >= 0; k--) + { + bit7 = (val >> (k * 7)) & 0x7F; + if (bit7 || first || !k) + { + if (k) + bit7 |= 0x80; + if (max_len > (*der_len)) + der[*der_len] = bit7; + (*der_len)++; + first = 1; + } + } + + } + n_start = n_end + 1; + } + + asn1_length_der (*der_len, NULL, &len_len); + if (max_len >= (*der_len + len_len)) + { + memmove (der + len_len, der, *der_len); + asn1_length_der (*der_len, der, &len_len); + } + *der_len += len_len; + + _asn1_afree (temp); + + if (max_len < (*der_len)) + return ASN1_MEM_ERROR; + + return ASN1_SUCCESS; +} + + +const char bit_mask[] = { 0xFF, 0xFE, 0xFC, 0xF8, 0xF0, 0xE0, 0xC0, 0x80 }; + +/** + * asn1_bit_der: + * @str: BIT string. + * @bit_len: number of meaningful bits in STR. + * @der: string returned. + * @der_len: number of meaningful bytes of DER + * (der[0]..der[ans_len-1]). + * + * Creates the DER coding for a BIT STRING type (length and pad + * included). + **/ +void +asn1_bit_der (const unsigned char *str, int bit_len, + unsigned char *der, int *der_len) +{ + int len_len, len_byte, len_pad; + + if (der == NULL) + return; + len_byte = bit_len >> 3; + len_pad = 8 - (bit_len & 7); + if (len_pad == 8) + len_pad = 0; + else + len_byte++; + asn1_length_der (len_byte + 1, der, &len_len); + der[len_len] = len_pad; + memcpy (der + len_len + 1, str, len_byte); + der[len_len + len_byte] &= bit_mask[len_pad]; + *der_len = len_byte + len_len + 1; +} + + +/******************************************************/ +/* Function : _asn1_complete_explicit_tag */ +/* Description: add the length coding to the EXPLICIT */ +/* tags. */ +/* Parameters: */ +/* node: pointer to the tree element. */ +/* der: string with the DER coding of the whole tree*/ +/* counter: number of meaningful bytes of DER */ +/* (der[0]..der[*counter-1]). */ +/* max_len: size of der vector */ +/* Return: */ +/* ASN1_MEM_ERROR if der vector isn't big enough, */ +/* otherwise ASN1_SUCCESS. */ +/******************************************************/ +asn1_retCode +_asn1_complete_explicit_tag (node_asn * node, unsigned char *der, + int *counter, int *max_len) +{ + node_asn *p; + int is_tag_implicit, len2, len3; + unsigned char temp[SIZEOF_UNSIGNED_INT]; + + is_tag_implicit = 0; + + if (node->type & CONST_TAG) + { + p = node->down; + /* When there are nested tags we must complete them reverse to + the order they were created. This is because completing a tag + modifies all data within it, including the incomplete tags + which store buffer positions -- simon@josefsson.org 2002-09-06 + */ + while (p->right) + p = p->right; + while (p && p != node->down->left) + { + if (type_field (p->type) == TYPE_TAG) + { + if (p->type & CONST_EXPLICIT) + { + len2 = strtol (p->name, NULL, 10); + _asn1_set_name (p, NULL); + asn1_length_der (*counter - len2, temp, &len3); + if (len3 <= (*max_len)) + { + memmove (der + len2 + len3, der + len2, + *counter - len2); + memcpy (der + len2, temp, len3); + } + *max_len -= len3; + *counter += len3; + is_tag_implicit = 0; + } + else + { /* CONST_IMPLICIT */ + if (!is_tag_implicit) + { + is_tag_implicit = 1; + } + } + } + p = p->left; + } + } + + if (*max_len < 0) + return ASN1_MEM_ERROR; + + return ASN1_SUCCESS; +} + + +/******************************************************/ +/* Function : _asn1_insert_tag_der */ +/* Description: creates the DER coding of tags of one */ +/* NODE. */ +/* Parameters: */ +/* node: pointer to the tree element. */ +/* der: string returned */ +/* counter: number of meaningful bytes of DER */ +/* (counter[0]..der[*counter-1]). */ +/* max_len: size of der vector */ +/* Return: */ +/* ASN1_GENERIC_ERROR if the type is unknown, */ +/* ASN1_MEM_ERROR if der vector isn't big enough, */ +/* otherwise ASN1_SUCCESS. */ +/******************************************************/ +asn1_retCode +_asn1_insert_tag_der (node_asn * node, unsigned char *der, int *counter, + int *max_len) +{ + node_asn *p; + int tag_len, is_tag_implicit; + unsigned char class, class_implicit = 0, temp[SIZEOF_UNSIGNED_INT * 3 + 1]; + unsigned long tag_implicit = 0; + char tag_der[MAX_TAG_LEN]; + + is_tag_implicit = 0; + + if (node->type & CONST_TAG) + { + p = node->down; + while (p) + { + if (type_field (p->type) == TYPE_TAG) + { + if (p->type & CONST_APPLICATION) + class = ASN1_CLASS_APPLICATION; + else if (p->type & CONST_UNIVERSAL) + class = ASN1_CLASS_UNIVERSAL; + else if (p->type & CONST_PRIVATE) + class = ASN1_CLASS_PRIVATE; + else + class = ASN1_CLASS_CONTEXT_SPECIFIC; + + if (p->type & CONST_EXPLICIT) + { + if (is_tag_implicit) + _asn1_tag_der (class_implicit, tag_implicit, tag_der, + &tag_len); + else + _asn1_tag_der (class | ASN1_CLASS_STRUCTURED, + strtoul (p->value, NULL, 10), tag_der, + &tag_len); + + *max_len -= tag_len; + if (*max_len >= 0) + memcpy (der + *counter, tag_der, tag_len); + *counter += tag_len; + + _asn1_ltostr (*counter, temp); + _asn1_set_name (p, temp); + + is_tag_implicit = 0; + } + else + { /* CONST_IMPLICIT */ + if (!is_tag_implicit) + { + if ((type_field (node->type) == TYPE_SEQUENCE) || + (type_field (node->type) == TYPE_SEQUENCE_OF) || + (type_field (node->type) == TYPE_SET) || + (type_field (node->type) == TYPE_SET_OF)) + class |= ASN1_CLASS_STRUCTURED; + class_implicit = class; + tag_implicit = strtoul (p->value, NULL, 10); + is_tag_implicit = 1; + } + } + } + p = p->right; + } + } + + if (is_tag_implicit) + { + _asn1_tag_der (class_implicit, tag_implicit, tag_der, &tag_len); + } + else + { + switch (type_field (node->type)) + { + case TYPE_NULL: + _asn1_tag_der (ASN1_CLASS_UNIVERSAL, ASN1_TAG_NULL, tag_der, + &tag_len); + break; + case TYPE_BOOLEAN: + _asn1_tag_der (ASN1_CLASS_UNIVERSAL, ASN1_TAG_BOOLEAN, tag_der, + &tag_len); + break; + case TYPE_INTEGER: + _asn1_tag_der (ASN1_CLASS_UNIVERSAL, ASN1_TAG_INTEGER, tag_der, + &tag_len); + break; + case TYPE_ENUMERATED: + _asn1_tag_der (ASN1_CLASS_UNIVERSAL, ASN1_TAG_ENUMERATED, tag_der, + &tag_len); + break; + case TYPE_OBJECT_ID: + _asn1_tag_der (ASN1_CLASS_UNIVERSAL, ASN1_TAG_OBJECT_ID, tag_der, + &tag_len); + break; + case TYPE_TIME: + if (node->type & CONST_UTC) + { + _asn1_tag_der (ASN1_CLASS_UNIVERSAL, ASN1_TAG_UTCTime, tag_der, + &tag_len); + } + else + _asn1_tag_der (ASN1_CLASS_UNIVERSAL, ASN1_TAG_GENERALIZEDTime, + tag_der, &tag_len); + break; + case TYPE_OCTET_STRING: + _asn1_tag_der (ASN1_CLASS_UNIVERSAL, ASN1_TAG_OCTET_STRING, tag_der, + &tag_len); + break; + case TYPE_GENERALSTRING: + _asn1_tag_der (ASN1_CLASS_UNIVERSAL, ASN1_TAG_GENERALSTRING, + tag_der, &tag_len); + break; + case TYPE_BIT_STRING: + _asn1_tag_der (ASN1_CLASS_UNIVERSAL, ASN1_TAG_BIT_STRING, tag_der, + &tag_len); + break; + case TYPE_SEQUENCE: + case TYPE_SEQUENCE_OF: + _asn1_tag_der (ASN1_CLASS_UNIVERSAL | ASN1_CLASS_STRUCTURED, + ASN1_TAG_SEQUENCE, tag_der, &tag_len); + break; + case TYPE_SET: + case TYPE_SET_OF: + _asn1_tag_der (ASN1_CLASS_UNIVERSAL | ASN1_CLASS_STRUCTURED, + ASN1_TAG_SET, tag_der, &tag_len); + break; + case TYPE_TAG: + tag_len = 0; + break; + case TYPE_CHOICE: + tag_len = 0; + break; + case TYPE_ANY: + tag_len = 0; + break; + default: + return ASN1_GENERIC_ERROR; + } + } + + *max_len -= tag_len; + if (*max_len >= 0) + memcpy (der + *counter, tag_der, tag_len); + *counter += tag_len; + + if (*max_len < 0) + return ASN1_MEM_ERROR; + + return ASN1_SUCCESS; +} + +/******************************************************/ +/* Function : _asn1_ordering_set */ +/* Description: puts the elements of a SET type in */ +/* the correct order according to DER rules. */ +/* Parameters: */ +/* der: string with the DER coding. */ +/* node: pointer to the SET element. */ +/* Return: */ +/******************************************************/ +void +_asn1_ordering_set (unsigned char *der, int der_len, node_asn * node) +{ + struct vet + { + int end; + unsigned long value; + struct vet *next, *prev; + }; + + int counter, len, len2; + struct vet *first, *last, *p_vet, *p2_vet; + node_asn *p; + unsigned char class, *temp; + unsigned long tag; + + counter = 0; + + if (type_field (node->type) != TYPE_SET) + return; + + p = node->down; + while ((type_field (p->type) == TYPE_TAG) + || (type_field (p->type) == TYPE_SIZE)) + p = p->right; + + if ((p == NULL) || (p->right == NULL)) + return; + + first = last = NULL; + while (p) + { + p_vet = (struct vet *) _asn1_alloca (sizeof (struct vet)); + if (p_vet == NULL) + return; + + p_vet->next = NULL; + p_vet->prev = last; + if (first == NULL) + first = p_vet; + else + last->next = p_vet; + last = p_vet; + + /* tag value calculation */ + if (asn1_get_tag_der + (der + counter, der_len - counter, &class, &len2, + &tag) != ASN1_SUCCESS) + return; + p_vet->value = (class << 24) | tag; + counter += len2; + + /* extraction and length */ + len2 = asn1_get_length_der (der + counter, der_len - counter, &len); + if (len2 < 0) + return; + counter += len + len2; + + p_vet->end = counter; + p = p->right; + } + + p_vet = first; + + while (p_vet) + { + p2_vet = p_vet->next; + counter = 0; + while (p2_vet) + { + if (p_vet->value > p2_vet->value) + { + /* change position */ + temp = (unsigned char *) _asn1_alloca (p_vet->end - counter); + if (temp == NULL) + return; + + memcpy (temp, der + counter, p_vet->end - counter); + memcpy (der + counter, der + p_vet->end, + p2_vet->end - p_vet->end); + memcpy (der + counter + p2_vet->end - p_vet->end, temp, + p_vet->end - counter); + _asn1_afree (temp); + + tag = p_vet->value; + p_vet->value = p2_vet->value; + p2_vet->value = tag; + + p_vet->end = counter + (p2_vet->end - p_vet->end); + } + counter = p_vet->end; + + p2_vet = p2_vet->next; + p_vet = p_vet->next; + } + + if (p_vet != first) + p_vet->prev->next = NULL; + else + first = NULL; + _asn1_afree (p_vet); + p_vet = first; + } +} + +/******************************************************/ +/* Function : _asn1_ordering_set_of */ +/* Description: puts the elements of a SET OF type in */ +/* the correct order according to DER rules. */ +/* Parameters: */ +/* der: string with the DER coding. */ +/* node: pointer to the SET OF element. */ +/* Return: */ +/******************************************************/ +void +_asn1_ordering_set_of (unsigned char *der, int der_len, node_asn * node) +{ + struct vet + { + int end; + struct vet *next, *prev; + }; + + int counter, len, len2, change; + struct vet *first, *last, *p_vet, *p2_vet; + node_asn *p; + unsigned char *temp, class; + unsigned long k, max; + + counter = 0; + + if (type_field (node->type) != TYPE_SET_OF) + return; + + p = node->down; + while ((type_field (p->type) == TYPE_TAG) + || (type_field (p->type) == TYPE_SIZE)) + p = p->right; + p = p->right; + + if ((p == NULL) || (p->right == NULL)) + return; + + first = last = NULL; + while (p) + { + p_vet = (struct vet *) _asn1_alloca (sizeof (struct vet)); + if (p_vet == NULL) + return; + + p_vet->next = NULL; + p_vet->prev = last; + if (first == NULL) + first = p_vet; + else + last->next = p_vet; + last = p_vet; + + /* extraction of tag and length */ + if (der_len - counter > 0) + { + + if (asn1_get_tag_der + (der + counter, der_len - counter, &class, &len, + NULL) != ASN1_SUCCESS) + return; + counter += len; + + len2 = asn1_get_length_der (der + counter, der_len - counter, &len); + if (len2 < 0) + return; + counter += len + len2; + } + + p_vet->end = counter; + p = p->right; + } + + p_vet = first; + + while (p_vet) + { + p2_vet = p_vet->next; + counter = 0; + while (p2_vet) + { + if ((p_vet->end - counter) > (p2_vet->end - p_vet->end)) + max = p_vet->end - counter; + else + max = p2_vet->end - p_vet->end; + + change = -1; + for (k = 0; k < max; k++) + if (der[counter + k] > der[p_vet->end + k]) + { + change = 1; + break; + } + else if (der[counter + k] < der[p_vet->end + k]) + { + change = 0; + break; + } + + if ((change == -1) + && ((p_vet->end - counter) > (p2_vet->end - p_vet->end))) + change = 1; + + if (change == 1) + { + /* change position */ + temp = (unsigned char *) _asn1_alloca (p_vet->end - counter); + if (temp == NULL) + return; + + memcpy (temp, der + counter, (p_vet->end) - counter); + memcpy (der + counter, der + (p_vet->end), + (p2_vet->end) - (p_vet->end)); + memcpy (der + counter + (p2_vet->end) - (p_vet->end), temp, + (p_vet->end) - counter); + _asn1_afree (temp); + + p_vet->end = counter + (p2_vet->end - p_vet->end); + } + counter = p_vet->end; + + p2_vet = p2_vet->next; + p_vet = p_vet->next; + } + + if (p_vet != first) + p_vet->prev->next = NULL; + else + first = NULL; + _asn1_afree (p_vet); + p_vet = first; + } +} + +/** + * asn1_der_coding - Creates the DER encoding for the NAME structure + * @element: pointer to an ASN1 element + * @name: the name of the structure you want to encode (it must be + * inside *POINTER). + * @ider: vector that will contain the DER encoding. DER must be a + * pointer to memory cells already allocated. + * @len: number of bytes of *@ider: @ider[0]..@ider[len-1], Initialy + * holds the sizeof of der vector. + * @errorDescription : return the error description or an empty + * string if success. + * + * Creates the DER encoding for the NAME structure (inside *POINTER + * structure). + * + * Returns: + * + * ASN1_SUCCESS: DER encoding OK. + * + * ASN1_ELEMENT_NOT_FOUND: NAME is not a valid element. + * + * ASN1_VALUE_NOT_FOUND: There is an element without a value. + * + * ASN1_MEM_ERROR: @ider vector isn't big enough. Also in this case + * LEN will contain the length needed. + * + **/ +asn1_retCode +asn1_der_coding (ASN1_TYPE element, const char *name, void *ider, int *len, + char *ErrorDescription) +{ + node_asn *node, *p, *p2; + char temp[SIZEOF_UNSIGNED_LONG_INT * 3 + 1]; + int counter, counter_old, len2, len3, tlen, move, max_len, max_len_old; + asn1_retCode err; + unsigned char *der = ider; + + node = asn1_find_node (element, name); + if (node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + /* Node is now a locally allocated variable. + * That is because in some point we modify the + * structure, and I don't know why! --nmav + */ + node = _asn1_copy_structure3 (node); + if (node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + max_len = *len; + + counter = 0; + move = DOWN; + p = node; + while (1) + { + + counter_old = counter; + max_len_old = max_len; + if (move != UP) + { + err = _asn1_insert_tag_der (p, der, &counter, &max_len); + if (err != ASN1_SUCCESS && err != ASN1_MEM_ERROR) + goto error; + } + switch (type_field (p->type)) + { + case TYPE_NULL: + max_len--; + if (max_len >= 0) + der[counter] = 0; + counter++; + move = RIGHT; + break; + case TYPE_BOOLEAN: + if ((p->type & CONST_DEFAULT) && (p->value == NULL)) + { + counter = counter_old; + max_len = max_len_old; + } + else + { + if (p->value == NULL) + { + _asn1_error_description_value_not_found (p, + ErrorDescription); + err = ASN1_VALUE_NOT_FOUND; + goto error; + } + max_len -= 2; + if (max_len >= 0) + { + der[counter++] = 1; + if (p->value[0] == 'F') + der[counter++] = 0; + else + der[counter++] = 0xFF; + } + else + counter += 2; + } + move = RIGHT; + break; + case TYPE_INTEGER: + case TYPE_ENUMERATED: + if ((p->type & CONST_DEFAULT) && (p->value == NULL)) + { + counter = counter_old; + max_len = max_len_old; + } + else + { + if (p->value == NULL) + { + _asn1_error_description_value_not_found (p, + ErrorDescription); + err = ASN1_VALUE_NOT_FOUND; + goto error; + } + len2 = asn1_get_length_der (p->value, p->value_len, &len3); + if (len2 < 0) + { + err = ASN1_DER_ERROR; + goto error; + } + max_len -= len2 + len3; + if (max_len >= 0) + memcpy (der + counter, p->value, len3 + len2); + counter += len3 + len2; + } + move = RIGHT; + break; + case TYPE_OBJECT_ID: + if ((p->type & CONST_DEFAULT) && (p->value == NULL)) + { + counter = counter_old; + max_len = max_len_old; + } + else + { + if (p->value == NULL) + { + _asn1_error_description_value_not_found (p, + ErrorDescription); + err = ASN1_VALUE_NOT_FOUND; + goto error; + } + len2 = max_len; + err = _asn1_objectid_der (p->value, der + counter, &len2); + if (err != ASN1_SUCCESS && err != ASN1_MEM_ERROR) + goto error; + + max_len -= len2; + counter += len2; + } + move = RIGHT; + break; + case TYPE_TIME: + if (p->value == NULL) + { + _asn1_error_description_value_not_found (p, ErrorDescription); + err = ASN1_VALUE_NOT_FOUND; + goto error; + } + len2 = max_len; + err = _asn1_time_der (p->value, der + counter, &len2); + if (err != ASN1_SUCCESS && err != ASN1_MEM_ERROR) + goto error; + + max_len -= len2; + counter += len2; + move = RIGHT; + break; + case TYPE_OCTET_STRING: + if (p->value == NULL) + { + _asn1_error_description_value_not_found (p, ErrorDescription); + err = ASN1_VALUE_NOT_FOUND; + goto error; + } + len2 = asn1_get_length_der (p->value, p->value_len, &len3); + if (len2 < 0) + { + err = ASN1_DER_ERROR; + goto error; + } + max_len -= len2 + len3; + if (max_len >= 0) + memcpy (der + counter, p->value, len3 + len2); + counter += len3 + len2; + move = RIGHT; + break; + case TYPE_GENERALSTRING: + if (p->value == NULL) + { + _asn1_error_description_value_not_found (p, ErrorDescription); + err = ASN1_VALUE_NOT_FOUND; + goto error; + } + len2 = asn1_get_length_der (p->value, p->value_len, &len3); + if (len2 < 0) + { + err = ASN1_DER_ERROR; + goto error; + } + max_len -= len2 + len3; + if (max_len >= 0) + memcpy (der + counter, p->value, len3 + len2); + counter += len3 + len2; + move = RIGHT; + break; + case TYPE_BIT_STRING: + if (p->value == NULL) + { + _asn1_error_description_value_not_found (p, ErrorDescription); + err = ASN1_VALUE_NOT_FOUND; + goto error; + } + len2 = asn1_get_length_der (p->value, p->value_len, &len3); + if (len2 < 0) + { + err = ASN1_DER_ERROR; + goto error; + } + max_len -= len2 + len3; + if (max_len >= 0) + memcpy (der + counter, p->value, len3 + len2); + counter += len3 + len2; + move = RIGHT; + break; + case TYPE_SEQUENCE: + case TYPE_SET: + if (move != UP) + { + _asn1_ltostr (counter, temp); + tlen = strlen (temp); + if (tlen > 0) + _asn1_set_value (p, temp, tlen + 1); + if (p->down == NULL) + { + move = UP; + continue; + } + else + { + p2 = p->down; + while (p2 && (type_field (p2->type) == TYPE_TAG)) + p2 = p2->right; + if (p2) + { + p = p2; + move = RIGHT; + continue; + } + move = UP; + continue; + } + } + else + { /* move==UP */ + len2 = strtol (p->value, NULL, 10); + _asn1_set_value (p, NULL, 0); + if ((type_field (p->type) == TYPE_SET) && (max_len >= 0)) + _asn1_ordering_set (der + len2, max_len - len2, p); + asn1_length_der (counter - len2, temp, &len3); + max_len -= len3; + if (max_len >= 0) + { + memmove (der + len2 + len3, der + len2, counter - len2); + memcpy (der + len2, temp, len3); + } + counter += len3; + move = RIGHT; + } + break; + case TYPE_SEQUENCE_OF: + case TYPE_SET_OF: + if (move != UP) + { + _asn1_ltostr (counter, temp); + tlen = strlen (temp); + + if (tlen > 0) + _asn1_set_value (p, temp, tlen + 1); + p = p->down; + while ((type_field (p->type) == TYPE_TAG) + || (type_field (p->type) == TYPE_SIZE)) + p = p->right; + if (p->right) + { + p = p->right; + move = RIGHT; + continue; + } + else + p = _asn1_find_up (p); + move = UP; + } + if (move == UP) + { + len2 = strtol (p->value, NULL, 10); + _asn1_set_value (p, NULL, 0); + if ((type_field (p->type) == TYPE_SET_OF) + && (max_len - len2 > 0)) + { + _asn1_ordering_set_of (der + len2, max_len - len2, p); + } + asn1_length_der (counter - len2, temp, &len3); + max_len -= len3; + if (max_len >= 0) + { + memmove (der + len2 + len3, der + len2, counter - len2); + memcpy (der + len2, temp, len3); + } + counter += len3; + move = RIGHT; + } + break; + case TYPE_ANY: + if (p->value == NULL) + { + _asn1_error_description_value_not_found (p, ErrorDescription); + err = ASN1_VALUE_NOT_FOUND; + goto error; + } + len2 = asn1_get_length_der (p->value, p->value_len, &len3); + if (len2 < 0) + { + err = ASN1_DER_ERROR; + goto error; + } + max_len -= len2; + if (max_len >= 0) + memcpy (der + counter, p->value + len3, len2); + counter += len2; + move = RIGHT; + break; + default: + move = (move == UP) ? RIGHT : DOWN; + break; + } + + if ((move != DOWN) && (counter != counter_old)) + { + err = _asn1_complete_explicit_tag (p, der, &counter, &max_len); + if (err != ASN1_SUCCESS && err != ASN1_MEM_ERROR) + goto error; + } + + if (p == node && move != DOWN) + break; + + if (move == DOWN) + { + if (p->down) + p = p->down; + else + move = RIGHT; + } + if (move == RIGHT) + { + if (p->right) + p = p->right; + else + move = UP; + } + if (move == UP) + p = _asn1_find_up (p); + } + + *len = counter; + + if (max_len < 0) + { + err = ASN1_MEM_ERROR; + goto error; + } + + err = ASN1_SUCCESS; + +error: + asn1_delete_structure (&node); + return err; +} diff --git a/src/daemon/https/minitasn1/decoding.c b/src/daemon/https/minitasn1/decoding.c new file mode 100644 index 00000000..0e00cd92 --- /dev/null +++ b/src/daemon/https/minitasn1/decoding.c @@ -0,0 +1,2821 @@ +/* + * Copyright (C) 2004, 2006 Free Software Foundation + * Copyright (C) 2002 Fabio Fiorina + * + * This file is part of LIBTASN1. + * + * The LIBTASN1 library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +/*****************************************************/ +/* File: decoding.c */ +/* Description: Functions to manage DER decoding */ +/*****************************************************/ + +#include +#include +#include "parser_aux.h" +#include +#include "structure.h" +#include "element.h" + + +void +_asn1_error_description_tag_error (node_asn * node, char *ErrorDescription) +{ + + Estrcpy (ErrorDescription, ":: tag error near element '"); + _asn1_hierarchical_name (node, ErrorDescription + strlen (ErrorDescription), + MAX_ERROR_DESCRIPTION_SIZE - 40); + Estrcat (ErrorDescription, "'"); + +} + +/** + * asn1_get_length_der: + * @der: DER data to decode. + * @der_len: Length of DER data to decode. + * @len: Output variable containing the length of the DER length field. + * + * Extract a length field from DER data. + * + * Return value: Return the decoded length value, or -1 on indefinite + * length, or -2 when the value was too big. + **/ +signed long +asn1_get_length_der (const unsigned char *der, int der_len, int *len) +{ + unsigned long ans; + int k, punt; + + *len = 0; + if (der_len <= 0) + return 0; + + if (!(der[0] & 128)) + { + /* short form */ + *len = 1; + return der[0]; + } + else + { + /* Long form */ + k = der[0] & 0x7F; + punt = 1; + if (k) + { /* definite length method */ + ans = 0; + while (punt <= k && punt < der_len) + { + unsigned long last = ans; + + ans = ans * 256 + der[punt++]; + if (ans < last) + /* we wrapped around, no bignum support... */ + return -2; + } + } + else + { /* indefinite length method */ + ans = -1; + } + + *len = punt; + return ans; + } +} + + + + +/** + * asn1_get_tag_der: + * @der: DER data to decode. + * @der_len: Length of DER data to decode. + * @cls: Output variable containing decoded class. + * @len: Output variable containing the length of the DER TAG data. + * @tag: Output variable containing the decoded tag. + * + * Decode the class and TAG from DER code. + * + * Return value: Returns ASN1_SUCCESS on success, or an error. + **/ +int +asn1_get_tag_der (const unsigned char *der, int der_len, + unsigned char *cls, int *len, unsigned long *tag) +{ + int punt, ris; + + if (der == NULL || der_len <= 0 || len == NULL) + return ASN1_DER_ERROR; + + *cls = der[0] & 0xE0; + if ((der[0] & 0x1F) != 0x1F) + { + /* short form */ + *len = 1; + ris = der[0] & 0x1F; + } + else + { + /* Long form */ + punt = 1; + ris = 0; + while (punt <= der_len && der[punt] & 128) + { + int last = ris; + ris = ris * 128 + (der[punt++] & 0x7F); + if (ris < last) + /* wrapper around, and no bignums... */ + return ASN1_DER_ERROR; + } + if (punt >= der_len) + return ASN1_DER_ERROR; + { + int last = ris; + ris = ris * 128 + (der[punt++] & 0x7F); + if (ris < last) + /* wrapper around, and no bignums... */ + return ASN1_DER_ERROR; + } + *len = punt; + } + if (tag) + *tag = ris; + return ASN1_SUCCESS; +} + + + + +/** + * asn1_get_octet_der: + * @der: DER data to decode containing the OCTET SEQUENCE. + * @der_len: Length of DER data to decode. + * @ret_len: Output variable containing the length of the DER data. + * @str: Pre-allocated output buffer to put decoded OCTET SEQUENCE in. + * @str_size: Length of pre-allocated output buffer. + * @str_len: Output variable containing the length of the OCTET SEQUENCE. + * + * Extract an OCTET SEQUENCE from DER data. + * + * Return value: Returns ASN1_SUCCESS on success, or an error. + **/ +int +asn1_get_octet_der (const unsigned char *der, int der_len, + int *ret_len, unsigned char *str, int str_size, + int *str_len) +{ + int len_len; + + if (der_len <= 0) + return ASN1_GENERIC_ERROR; + + /* if(str==NULL) return ASN1_SUCCESS; */ + *str_len = asn1_get_length_der (der, der_len, &len_len); + + if (*str_len < 0) + return ASN1_DER_ERROR; + + *ret_len = *str_len + len_len; + if (str_size >= *str_len) + memcpy (str, der + len_len, *str_len); + else + { + return ASN1_MEM_ERROR; + } + + return ASN1_SUCCESS; +} + + + +/* Returns ASN1_SUCCESS on success or an error code on error. + */ +int +_asn1_get_time_der (const unsigned char *der, int der_len, int *ret_len, + char *str, int str_size) +{ + int len_len, str_len; + + if (der_len <= 0 || str == NULL) + return ASN1_DER_ERROR; + str_len = asn1_get_length_der (der, der_len, &len_len); + if (str_len < 0 || str_size < str_len) + return ASN1_DER_ERROR; + memcpy (str, der + len_len, str_len); + str[str_len] = 0; + *ret_len = str_len + len_len; + + return ASN1_SUCCESS; +} + + + +void +_asn1_get_objectid_der (const unsigned char *der, int der_len, int *ret_len, + char *str, int str_size) +{ + int len_len, len, k; + char temp[20]; + unsigned long val, val1; + + *ret_len = 0; + if (str && str_size > 0) + str[0] = 0; /* no oid */ + + if (str == NULL || der_len <= 0) + return; + len = asn1_get_length_der (der, der_len, &len_len); + + if (len < 0 || len > der_len || len_len > der_len) + return; + + val1 = der[len_len] / 40; + val = der[len_len] - val1 * 40; + + _asn1_str_cpy (str, str_size, _asn1_ltostr (val1, temp)); + _asn1_str_cat (str, str_size, "."); + _asn1_str_cat (str, str_size, _asn1_ltostr (val, temp)); + + val = 0; + for (k = 1; k < len; k++) + { + val = val << 7; + val |= der[len_len + k] & 0x7F; + if (!(der[len_len + k] & 0x80)) + { + _asn1_str_cat (str, str_size, "."); + _asn1_str_cat (str, str_size, _asn1_ltostr (val, temp)); + val = 0; + } + } + *ret_len = len + len_len; +} + + + + +/** + * asn1_get_bit_der: + * @der: DER data to decode containing the BIT SEQUENCE. + * @der_len: Length of DER data to decode. + * @ret_len: Output variable containing the length of the DER data. + * @str: Pre-allocated output buffer to put decoded BIT SEQUENCE in. + * @str_size: Length of pre-allocated output buffer. + * @bit_len: Output variable containing the size of the BIT SEQUENCE. + * + * Extract a BIT SEQUENCE from DER data. + * + * Return value: Return ASN1_SUCCESS on success, or an error. + **/ +int +asn1_get_bit_der (const unsigned char *der, int der_len, + int *ret_len, unsigned char *str, int str_size, + int *bit_len) +{ + int len_len, len_byte; + + if (der_len <= 0) + return ASN1_GENERIC_ERROR; + len_byte = asn1_get_length_der (der, der_len, &len_len) - 1; + if (len_byte < 0) + return ASN1_DER_ERROR; + + *ret_len = len_byte + len_len + 1; + *bit_len = len_byte * 8 - der[len_len]; + + if (str_size >= len_byte) + memcpy (str, der + len_len + 1, len_byte); + else + { + return ASN1_MEM_ERROR; + } + + return ASN1_SUCCESS; +} + + + + +int +_asn1_extract_tag_der (node_asn * node, const unsigned char *der, int der_len, + int *ret_len) +{ + node_asn *p; + int counter, len2, len3, is_tag_implicit; + unsigned long tag, tag_implicit = 0; + unsigned char class, class2, class_implicit = 0; + + if (der_len <= 0) + return ASN1_GENERIC_ERROR; + + counter = is_tag_implicit = 0; + + if (node->type & CONST_TAG) + { + p = node->down; + while (p) + { + if (type_field (p->type) == TYPE_TAG) + { + if (p->type & CONST_APPLICATION) + class2 = ASN1_CLASS_APPLICATION; + else if (p->type & CONST_UNIVERSAL) + class2 = ASN1_CLASS_UNIVERSAL; + else if (p->type & CONST_PRIVATE) + class2 = ASN1_CLASS_PRIVATE; + else + class2 = ASN1_CLASS_CONTEXT_SPECIFIC; + + if (p->type & CONST_EXPLICIT) + { + if (asn1_get_tag_der + (der + counter, der_len - counter, &class, &len2, + &tag) != ASN1_SUCCESS) + return ASN1_DER_ERROR; + if (counter + len2 > der_len) + return ASN1_DER_ERROR; + counter += len2; + len3 = + asn1_get_length_der (der + counter, der_len - counter, + &len2); + if (len3 < 0) + return ASN1_DER_ERROR; + counter += len2; + if (!is_tag_implicit) + { + if ((class != (class2 | ASN1_CLASS_STRUCTURED)) || + (tag != strtoul ((char *) p->value, NULL, 10))) + return ASN1_TAG_ERROR; + } + else + { /* ASN1_TAG_IMPLICIT */ + if ((class != class_implicit) || (tag != tag_implicit)) + return ASN1_TAG_ERROR; + } + + is_tag_implicit = 0; + } + else + { /* ASN1_TAG_IMPLICIT */ + if (!is_tag_implicit) + { + if ((type_field (node->type) == TYPE_SEQUENCE) || + (type_field (node->type) == TYPE_SEQUENCE_OF) || + (type_field (node->type) == TYPE_SET) || + (type_field (node->type) == TYPE_SET_OF)) + class2 |= ASN1_CLASS_STRUCTURED; + class_implicit = class2; + tag_implicit = strtoul ((char *) p->value, NULL, 10); + is_tag_implicit = 1; + } + } + } + p = p->right; + } + } + + if (is_tag_implicit) + { + if (asn1_get_tag_der + (der + counter, der_len - counter, &class, &len2, + &tag) != ASN1_SUCCESS) + return ASN1_DER_ERROR; + if (counter + len2 > der_len) + return ASN1_DER_ERROR; + + if ((class != class_implicit) || (tag != tag_implicit)) + { + if (type_field (node->type) == TYPE_OCTET_STRING) + { + class_implicit |= ASN1_CLASS_STRUCTURED; + if ((class != class_implicit) || (tag != tag_implicit)) + return ASN1_TAG_ERROR; + } + else + return ASN1_TAG_ERROR; + } + } + else + { + if (type_field (node->type) == TYPE_TAG) + { + counter = 0; + *ret_len = counter; + return ASN1_SUCCESS; + } + + if (asn1_get_tag_der + (der + counter, der_len - counter, &class, &len2, + &tag) != ASN1_SUCCESS) + return ASN1_DER_ERROR; + if (counter + len2 > der_len) + return ASN1_DER_ERROR; + + switch (type_field (node->type)) + { + case TYPE_NULL: + if ((class != ASN1_CLASS_UNIVERSAL) || (tag != ASN1_TAG_NULL)) + return ASN1_DER_ERROR; + break; + case TYPE_BOOLEAN: + if ((class != ASN1_CLASS_UNIVERSAL) || (tag != ASN1_TAG_BOOLEAN)) + return ASN1_DER_ERROR; + break; + case TYPE_INTEGER: + if ((class != ASN1_CLASS_UNIVERSAL) || (tag != ASN1_TAG_INTEGER)) + return ASN1_DER_ERROR; + break; + case TYPE_ENUMERATED: + if ((class != ASN1_CLASS_UNIVERSAL) || (tag != ASN1_TAG_ENUMERATED)) + return ASN1_DER_ERROR; + break; + case TYPE_OBJECT_ID: + if ((class != ASN1_CLASS_UNIVERSAL) || (tag != ASN1_TAG_OBJECT_ID)) + return ASN1_DER_ERROR; + break; + case TYPE_TIME: + if (node->type & CONST_UTC) + { + if ((class != ASN1_CLASS_UNIVERSAL) + || (tag != ASN1_TAG_UTCTime)) + return ASN1_DER_ERROR; + } + else + { + if ((class != ASN1_CLASS_UNIVERSAL) + || (tag != ASN1_TAG_GENERALIZEDTime)) + return ASN1_DER_ERROR; + } + break; + case TYPE_OCTET_STRING: + if (((class != ASN1_CLASS_UNIVERSAL) + && (class != (ASN1_CLASS_UNIVERSAL | ASN1_CLASS_STRUCTURED))) + || (tag != ASN1_TAG_OCTET_STRING)) + return ASN1_DER_ERROR; + break; + case TYPE_GENERALSTRING: + if ((class != ASN1_CLASS_UNIVERSAL) + || (tag != ASN1_TAG_GENERALSTRING)) + return ASN1_DER_ERROR; + break; + case TYPE_BIT_STRING: + if ((class != ASN1_CLASS_UNIVERSAL) || (tag != ASN1_TAG_BIT_STRING)) + return ASN1_DER_ERROR; + break; + case TYPE_SEQUENCE: + case TYPE_SEQUENCE_OF: + if ((class != (ASN1_CLASS_UNIVERSAL | ASN1_CLASS_STRUCTURED)) + || (tag != ASN1_TAG_SEQUENCE)) + return ASN1_DER_ERROR; + break; + case TYPE_SET: + case TYPE_SET_OF: + if ((class != (ASN1_CLASS_UNIVERSAL | ASN1_CLASS_STRUCTURED)) + || (tag != ASN1_TAG_SET)) + return ASN1_DER_ERROR; + break; + case TYPE_ANY: + counter -= len2; + break; + default: + return ASN1_DER_ERROR; + break; + } + } + + counter += len2; + *ret_len = counter; + return ASN1_SUCCESS; +} + + +int +_asn1_delete_not_used (node_asn * node) +{ + node_asn *p, *p2; + + if (node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + p = node; + while (p) + { + if (p->type & CONST_NOT_USED) + { + p2 = NULL; + if (p != node) + { + p2 = _asn1_find_left (p); + if (!p2) + p2 = _asn1_find_up (p); + } + asn1_delete_structure (&p); + p = p2; + } + + if (!p) + break; /* reach node */ + + if (p->down) + { + p = p->down; + } + else + { + if (p == node) + p = NULL; + else if (p->right) + p = p->right; + else + { + while (1) + { + p = _asn1_find_up (p); + if (p == node) + { + p = NULL; + break; + } + if (p->right) + { + p = p->right; + break; + } + } + } + } + } + return ASN1_SUCCESS; +} + + +asn1_retCode +_asn1_get_octet_string (const unsigned char *der, node_asn * node, int *len) +{ + int len2, len3, counter, counter2, counter_end, tot_len, indefinite; + unsigned char *temp, *temp2; + + counter = 0; + + if (*(der - 1) & ASN1_CLASS_STRUCTURED) + { + tot_len = 0; + indefinite = asn1_get_length_der (der, *len, &len3); + if (indefinite < -1) + return ASN1_DER_ERROR; + + counter += len3; + if (indefinite >= 0) + indefinite += len3; + + while (1) + { + if (counter > (*len)) + return ASN1_DER_ERROR; + + if (indefinite == -1) + { + if ((der[counter] == 0) && (der[counter + 1] == 0)) + { + counter += 2; + break; + } + } + else if (counter >= indefinite) + break; + + if (der[counter] != ASN1_TAG_OCTET_STRING) + return ASN1_DER_ERROR; + + counter++; + + len2 = asn1_get_length_der (der + counter, *len - counter, &len3); + if (len2 <= 0) + return ASN1_DER_ERROR; + + counter += len3 + len2; + tot_len += len2; + } + + /* copy */ + if (node) + { + asn1_length_der (tot_len, NULL, &len2); + temp = _asn1_alloca (len2 + tot_len); + if (temp == NULL) + { + return ASN1_MEM_ALLOC_ERROR; + } + + asn1_length_der (tot_len, temp, &len2); + tot_len += len2; + temp2 = temp + len2; + len2 = asn1_get_length_der (der, *len, &len3); + if (len2 < -1) + return ASN1_DER_ERROR; + counter2 = len3 + 1; + + if (indefinite == -1) + counter_end = counter - 2; + else + counter_end = counter; + + while (counter2 < counter_end) + { + len2 = + asn1_get_length_der (der + counter2, *len - counter, &len3); + if (len2 < -1) + return ASN1_DER_ERROR; + + /* FIXME: to be checked. Is this ok? Has the + * size been checked before? + */ + memcpy (temp2, der + counter2 + len3, len2); + temp2 += len2; + counter2 += len2 + len3 + 1; + } + + _asn1_set_value (node, temp, tot_len); + _asn1_afree (temp); + } + } + else + { /* NOT STRUCTURED */ + len2 = asn1_get_length_der (der, *len, &len3); + if (len2 < 0) + return ASN1_DER_ERROR; + if (len3 + len2 > *len) + return ASN1_DER_ERROR; + if (node) + _asn1_set_value (node, der, len3 + len2); + counter = len3 + len2; + } + + *len = counter; + return ASN1_SUCCESS; + +} + + +asn1_retCode +_asn1_get_indefinite_length_string (const unsigned char *der, int *len) +{ + int len2, len3, counter, indefinite; + unsigned long tag; + unsigned char class; + + counter = indefinite = 0; + + while (1) + { + if ((*len) < counter) + return ASN1_DER_ERROR; + + if ((der[counter] == 0) && (der[counter + 1] == 0)) + { + counter += 2; + indefinite--; + if (indefinite <= 0) + break; + else + continue; + } + + if (asn1_get_tag_der + (der + counter, *len - counter, &class, &len2, + &tag) != ASN1_SUCCESS) + return ASN1_DER_ERROR; + if (counter + len2 > *len) + return ASN1_DER_ERROR; + counter += len2; + len2 = asn1_get_length_der (der + counter, *len - counter, &len3); + if (len2 < -1) + return ASN1_DER_ERROR; + if (len2 == -1) + { + indefinite++; + counter += 1; + } + else + { + counter += len2 + len3; + } + } + + *len = counter; + return ASN1_SUCCESS; + +} + + +/** + * asn1_der_decoding - Fill the structure *ELEMENT with values of a DER encoding string. + * @element: pointer to an ASN1 structure. + * @ider: vector that contains the DER encoding. + * @len: number of bytes of *@ider: @ider[0]..@ider[len-1]. + * @errorDescription: null-terminated string contains details when an + * error occurred. + * + * Fill the structure *ELEMENT with values of a DER encoding + * string. The sructure must just be created with function + * 'create_stucture'. If an error occurs during the decoding + * procedure, the *ELEMENT is deleted and set equal to + * %ASN1_TYPE_EMPTY. + * + * Returns: + * + * ASN1_SUCCESS: DER encoding OK. + * + * ASN1_ELEMENT_NOT_FOUND: ELEMENT is ASN1_TYPE_EMPTY. + * + * ASN1_TAG_ERROR,ASN1_DER_ERROR: The der encoding doesn't match + * the structure NAME. *ELEMENT deleted. + **/ + +asn1_retCode +asn1_der_decoding (ASN1_TYPE * element, const void *ider, int len, + char *errorDescription) +{ + node_asn *node, *p, *p2, *p3; + char temp[128]; + int counter, len2, len3, len4, move, ris, tlen; + unsigned char class, *temp2; + unsigned long tag; + int indefinite, result; + const unsigned char *der = ider; + + node = *element; + + if (node == ASN1_TYPE_EMPTY) + return ASN1_ELEMENT_NOT_FOUND; + + if (node->type & CONST_OPTION) + { + asn1_delete_structure (element); + return ASN1_GENERIC_ERROR; + } + + counter = 0; + move = DOWN; + p = node; + while (1) + { + ris = ASN1_SUCCESS; + if (move != UP) + { + if (p->type & CONST_SET) + { + p2 = _asn1_find_up (p); + len2 = strtol (p2->value, NULL, 10); + if (len2 == -1) + { + if (!der[counter] && !der[counter + 1]) + { + p = p2; + move = UP; + counter += 2; + continue; + } + } + else if (counter == len2) + { + p = p2; + move = UP; + continue; + } + else if (counter > len2) + { + asn1_delete_structure (element); + return ASN1_DER_ERROR; + } + p2 = p2->down; + while (p2) + { + if ((p2->type & CONST_SET) && (p2->type & CONST_NOT_USED)) + { + if (type_field (p2->type) != TYPE_CHOICE) + ris = + _asn1_extract_tag_der (p2, der + counter, + len - counter, &len2); + else + { + p3 = p2->down; + while (p3) + { + ris = + _asn1_extract_tag_der (p3, der + counter, + len - counter, &len2); + if (ris == ASN1_SUCCESS) + break; + p3 = p3->right; + } + } + if (ris == ASN1_SUCCESS) + { + p2->type &= ~CONST_NOT_USED; + p = p2; + break; + } + } + p2 = p2->right; + } + if (p2 == NULL) + { + asn1_delete_structure (element); + return ASN1_DER_ERROR; + } + } + + if ((p->type & CONST_OPTION) || (p->type & CONST_DEFAULT)) + { + p2 = _asn1_find_up (p); + len2 = strtol (p2->value, NULL, 10); + if (counter == len2) + { + if (p->right) + { + p2 = p->right; + move = RIGHT; + } + else + move = UP; + + if (p->type & CONST_OPTION) + asn1_delete_structure (&p); + + p = p2; + continue; + } + } + + if (type_field (p->type) == TYPE_CHOICE) + { + while (p->down) + { + if (counter < len) + ris = + _asn1_extract_tag_der (p->down, der + counter, + len - counter, &len2); + else + ris = ASN1_DER_ERROR; + if (ris == ASN1_SUCCESS) + { + while (p->down->right) + { + p2 = p->down->right; + asn1_delete_structure (&p2); + } + break; + } + else if (ris == ASN1_ERROR_TYPE_ANY) + { + asn1_delete_structure (element); + return ASN1_ERROR_TYPE_ANY; + } + else + { + p2 = p->down; + asn1_delete_structure (&p2); + } + } + + if (p->down == NULL) + { + if (!(p->type & CONST_OPTION)) + { + asn1_delete_structure (element); + return ASN1_DER_ERROR; + } + } + else + p = p->down; + } + + if ((p->type & CONST_OPTION) || (p->type & CONST_DEFAULT)) + { + p2 = _asn1_find_up (p); + len2 = strtol (p2->value, NULL, 10); + if ((len2 != -1) && (counter > len2)) + ris = ASN1_TAG_ERROR; + } + + if (ris == ASN1_SUCCESS) + ris = + _asn1_extract_tag_der (p, der + counter, len - counter, &len2); + if (ris != ASN1_SUCCESS) + { + if (p->type & CONST_OPTION) + { + p->type |= CONST_NOT_USED; + move = RIGHT; + } + else if (p->type & CONST_DEFAULT) + { + _asn1_set_value (p, NULL, 0); + move = RIGHT; + } + else + { + if (errorDescription != NULL) + _asn1_error_description_tag_error (p, errorDescription); + + asn1_delete_structure (element); + return ASN1_TAG_ERROR; + } + } + else + counter += len2; + } + + if (ris == ASN1_SUCCESS) + { + switch (type_field (p->type)) + { + case TYPE_NULL: + if (der[counter]) + { + asn1_delete_structure (element); + return ASN1_DER_ERROR; + } + counter++; + move = RIGHT; + break; + case TYPE_BOOLEAN: + if (der[counter++] != 1) + { + asn1_delete_structure (element); + return ASN1_DER_ERROR; + } + if (der[counter++] == 0) + _asn1_set_value (p, "F", 1); + else + _asn1_set_value (p, "T", 1); + move = RIGHT; + break; + case TYPE_INTEGER: + case TYPE_ENUMERATED: + len2 = + asn1_get_length_der (der + counter, len - counter, &len3); + if (len2 < 0) + return ASN1_DER_ERROR; + if (len2 + len3 > len - counter) + return ASN1_DER_ERROR; + _asn1_set_value (p, der + counter, len3 + len2); + counter += len3 + len2; + move = RIGHT; + break; + case TYPE_OBJECT_ID: + _asn1_get_objectid_der (der + counter, len - counter, &len2, + temp, sizeof (temp)); + tlen = strlen (temp); + if (tlen > 0) + _asn1_set_value (p, temp, tlen + 1); + counter += len2; + move = RIGHT; + break; + case TYPE_TIME: + result = + _asn1_get_time_der (der + counter, len - counter, &len2, temp, + sizeof (temp) - 1); + if (result != ASN1_SUCCESS) + { + asn1_delete_structure (element); + return result; + } + tlen = strlen (temp); + if (tlen > 0) + _asn1_set_value (p, temp, tlen + 1); + counter += len2; + move = RIGHT; + break; + case TYPE_OCTET_STRING: + len3 = len - counter; + ris = _asn1_get_octet_string (der + counter, p, &len3); + if (ris != ASN1_SUCCESS) + return ris; + counter += len3; + move = RIGHT; + break; + case TYPE_GENERALSTRING: + len2 = + asn1_get_length_der (der + counter, len - counter, &len3); + if (len2 < 0) + return ASN1_DER_ERROR; + if (len3 + len2 > len - counter) + return ASN1_DER_ERROR; + _asn1_set_value (p, der + counter, len3 + len2); + counter += len3 + len2; + move = RIGHT; + break; + case TYPE_BIT_STRING: + len2 = + asn1_get_length_der (der + counter, len - counter, &len3); + if (len2 < 0) + return ASN1_DER_ERROR; + if (len3 + len2 > len - counter) + return ASN1_DER_ERROR; + _asn1_set_value (p, der + counter, len3 + len2); + counter += len3 + len2; + move = RIGHT; + break; + case TYPE_SEQUENCE: + case TYPE_SET: + if (move == UP) + { + len2 = strtol (p->value, NULL, 10); + _asn1_set_value (p, NULL, 0); + if (len2 == -1) + { /* indefinite length method */ + if (len - counter + 1 > 0) + { + if ((der[counter]) || der[counter + 1]) + { + asn1_delete_structure (element); + return ASN1_DER_ERROR; + } + } + else + return ASN1_DER_ERROR; + counter += 2; + } + else + { /* definite length method */ + if (len2 != counter) + { + asn1_delete_structure (element); + return ASN1_DER_ERROR; + } + } + move = RIGHT; + } + else + { /* move==DOWN || move==RIGHT */ + len3 = + asn1_get_length_der (der + counter, len - counter, &len2); + if (len3 < -1) + return ASN1_DER_ERROR; + counter += len2; + if (len3 > 0) + { + _asn1_ltostr (counter + len3, temp); + tlen = strlen (temp); + if (tlen > 0) + _asn1_set_value (p, temp, tlen + 1); + move = DOWN; + } + else if (len3 == 0) + { + p2 = p->down; + while (p2) + { + if (type_field (p2->type) != TYPE_TAG) + { + p3 = p2->right; + asn1_delete_structure (&p2); + p2 = p3; + } + else + p2 = p2->right; + } + move = RIGHT; + } + else + { /* indefinite length method */ + _asn1_set_value (p, "-1", 3); + move = DOWN; + } + } + break; + case TYPE_SEQUENCE_OF: + case TYPE_SET_OF: + if (move == UP) + { + len2 = strtol (p->value, NULL, 10); + if (len2 == -1) + { /* indefinite length method */ + if ((counter + 2) > len) + return ASN1_DER_ERROR; + if ((der[counter]) || der[counter + 1]) + { + _asn1_append_sequence_set (p); + p = p->down; + while (p->right) + p = p->right; + move = RIGHT; + continue; + } + _asn1_set_value (p, NULL, 0); + counter += 2; + } + else + { /* definite length method */ + if (len2 > counter) + { + _asn1_append_sequence_set (p); + p = p->down; + while (p->right) + p = p->right; + move = RIGHT; + continue; + } + _asn1_set_value (p, NULL, 0); + if (len2 != counter) + { + asn1_delete_structure (element); + return ASN1_DER_ERROR; + } + } + } + else + { /* move==DOWN || move==RIGHT */ + len3 = + asn1_get_length_der (der + counter, len - counter, &len2); + if (len3 < -1) + return ASN1_DER_ERROR; + counter += len2; + if (len3) + { + if (len3 > 0) + { /* definite length method */ + _asn1_ltostr (counter + len3, temp); + tlen = strlen (temp); + + if (tlen > 0) + _asn1_set_value (p, temp, tlen + 1); + } + else + { /* indefinite length method */ + _asn1_set_value (p, "-1", 3); + } + p2 = p->down; + while ((type_field (p2->type) == TYPE_TAG) + || (type_field (p2->type) == TYPE_SIZE)) + p2 = p2->right; + if (p2->right == NULL) + _asn1_append_sequence_set (p); + p = p2; + } + } + move = RIGHT; + break; + case TYPE_ANY: + if (asn1_get_tag_der + (der + counter, len - counter, &class, &len2, + &tag) != ASN1_SUCCESS) + return ASN1_DER_ERROR; + if (counter + len2 > len) + return ASN1_DER_ERROR; + len4 = + asn1_get_length_der (der + counter + len2, + len - counter - len2, &len3); + if (len4 < -1) + return ASN1_DER_ERROR; + if (len4 > len - counter + len2 + len3) + return ASN1_DER_ERROR; + if (len4 != -1) + { + len2 += len4; + asn1_length_der (len2 + len3, NULL, &len4); + temp2 = (unsigned char *) _asn1_alloca (len2 + len3 + len4); + if (temp2 == NULL) + { + asn1_delete_structure (element); + return ASN1_MEM_ALLOC_ERROR; + } + + asn1_octet_der (der + counter, len2 + len3, temp2, &len4); + _asn1_set_value (p, temp2, len4); + _asn1_afree (temp2); + counter += len2 + len3; + } + else + { /* indefinite length */ + /* Check indefinite lenth method in an EXPLICIT TAG */ + if ((p->type & CONST_TAG) && (der[counter - 1] == 0x80)) + indefinite = 1; + else + indefinite = 0; + + len2 = len - counter; + ris = + _asn1_get_indefinite_length_string (der + counter, &len2); + if (ris != ASN1_SUCCESS) + { + asn1_delete_structure (element); + return ris; + } + asn1_length_der (len2, NULL, &len4); + temp2 = (unsigned char *) _asn1_alloca (len2 + len4); + if (temp2 == NULL) + { + asn1_delete_structure (element); + return ASN1_MEM_ALLOC_ERROR; + } + + asn1_octet_der (der + counter, len2, temp2, &len4); + _asn1_set_value (p, temp2, len4); + _asn1_afree (temp2); + counter += len2; + + /* Check if a couple of 0x00 are present due to an EXPLICIT TAG with + an indefinite length method. */ + if (indefinite) + { + if (!der[counter] && !der[counter + 1]) + { + counter += 2; + } + else + { + asn1_delete_structure (element); + return ASN1_DER_ERROR; + } + } + } + move = RIGHT; + break; + default: + move = (move == UP) ? RIGHT : DOWN; + break; + } + } + + if (p == node && move != DOWN) + break; + + if (move == DOWN) + { + if (p->down) + p = p->down; + else + move = RIGHT; + } + if ((move == RIGHT) && !(p->type & CONST_SET)) + { + if (p->right) + p = p->right; + else + move = UP; + } + if (move == UP) + p = _asn1_find_up (p); + } + + _asn1_delete_not_used (*element); + + if (counter != len) + { + asn1_delete_structure (element); + return ASN1_DER_ERROR; + } + + return ASN1_SUCCESS; +} + + +#define FOUND 1 +#define SAME_BRANCH 2 +#define OTHER_BRANCH 3 +#define EXIT 4 + +/** + * asn1_der_decoding_element - Fill the element named ELEMENTNAME of the structure STRUCTURE with values of a DER encoding string. + * @structure: pointer to an ASN1 structure + * @elementName: name of the element to fill + * @ider: vector that contains the DER encoding of the whole structure. + * @len: number of bytes of *der: der[0]..der[len-1] + * @errorDescription: null-terminated string contains details when an + * error occurred. + * + * Fill the element named ELEMENTNAME with values of a DER encoding + * string. The sructure must just be created with function + * 'create_stucture'. The DER vector must contain the encoding + * string of the whole STRUCTURE. If an error occurs during the + * decoding procedure, the *STRUCTURE is deleted and set equal to + * %ASN1_TYPE_EMPTY. + * + * Returns: + * + * ASN1_SUCCESS: DER encoding OK. + * + * ASN1_ELEMENT_NOT_FOUND: ELEMENT is ASN1_TYPE_EMPTY or + * elementName == NULL. + * + * ASN1_TAG_ERROR,ASN1_DER_ERROR: The der encoding doesn't match + * the structure STRUCTURE. *ELEMENT deleted. + * + **/ +asn1_retCode +asn1_der_decoding_element (ASN1_TYPE * structure, const char *elementName, + const void *ider, int len, char *errorDescription) +{ + node_asn *node, *p, *p2, *p3, *nodeFound = ASN1_TYPE_EMPTY; + char temp[128], currentName[MAX_NAME_SIZE * 10], *dot_p, *char_p; + int nameLen = MAX_NAME_SIZE * 10 - 1, state; + int counter, len2, len3, len4, move, ris, tlen; + unsigned char class, *temp2; + unsigned long tag; + int indefinite, result; + const unsigned char *der = ider; + + node = *structure; + + if (node == ASN1_TYPE_EMPTY) + return ASN1_ELEMENT_NOT_FOUND; + + if (elementName == NULL) + { + asn1_delete_structure (structure); + return ASN1_ELEMENT_NOT_FOUND; + } + + if (node->type & CONST_OPTION) + { + asn1_delete_structure (structure); + return ASN1_GENERIC_ERROR; + } + + if ((*structure)->name) + { /* Has *structure got a name? */ + nameLen -= strlen ((*structure)->name); + if (nameLen > 0) + strcpy (currentName, (*structure)->name); + else + { + asn1_delete_structure (structure); + return ASN1_MEM_ERROR; + } + if (!(strcmp (currentName, elementName))) + { + state = FOUND; + nodeFound = *structure; + } + else if (!memcmp (currentName, elementName, strlen (currentName))) + state = SAME_BRANCH; + else + state = OTHER_BRANCH; + } + else + { /* *structure doesn't have a name? */ + currentName[0] = 0; + if (elementName[0] == 0) + { + state = FOUND; + nodeFound = *structure; + } + else + { + state = SAME_BRANCH; + } + } + + counter = 0; + move = DOWN; + p = node; + while (1) + { + + ris = ASN1_SUCCESS; + + if (move != UP) + { + if (p->type & CONST_SET) + { + p2 = _asn1_find_up (p); + len2 = strtol (p2->value, NULL, 10); + if (counter == len2) + { + p = p2; + move = UP; + continue; + } + else if (counter > len2) + { + asn1_delete_structure (structure); + return ASN1_DER_ERROR; + } + p2 = p2->down; + while (p2) + { + if ((p2->type & CONST_SET) && (p2->type & CONST_NOT_USED)) + { + if (type_field (p2->type) != TYPE_CHOICE) + ris = + _asn1_extract_tag_der (p2, der + counter, + len - counter, &len2); + else + { + p3 = p2->down; + while (p3) + { + ris = + _asn1_extract_tag_der (p3, der + counter, + len - counter, &len2); + if (ris == ASN1_SUCCESS) + break; + p3 = p3->right; + } + } + if (ris == ASN1_SUCCESS) + { + p2->type &= ~CONST_NOT_USED; + p = p2; + break; + } + } + p2 = p2->right; + } + if (p2 == NULL) + { + asn1_delete_structure (structure); + return ASN1_DER_ERROR; + } + } + + if ((p->type & CONST_OPTION) || (p->type & CONST_DEFAULT)) + { + p2 = _asn1_find_up (p); + len2 = strtol (p2->value, NULL, 10); + if (counter == len2) + { + if (p->right) + { + p2 = p->right; + move = RIGHT; + } + else + move = UP; + + if (p->type & CONST_OPTION) + asn1_delete_structure (&p); + + p = p2; + continue; + } + } + + if (type_field (p->type) == TYPE_CHOICE) + { + while (p->down) + { + if (counter < len) + ris = + _asn1_extract_tag_der (p->down, der + counter, + len - counter, &len2); + else + ris = ASN1_DER_ERROR; + if (ris == ASN1_SUCCESS) + { + while (p->down->right) + { + p2 = p->down->right; + asn1_delete_structure (&p2); + } + break; + } + else if (ris == ASN1_ERROR_TYPE_ANY) + { + asn1_delete_structure (structure); + return ASN1_ERROR_TYPE_ANY; + } + else + { + p2 = p->down; + asn1_delete_structure (&p2); + } + } + + if (p->down == NULL) + { + if (!(p->type & CONST_OPTION)) + { + asn1_delete_structure (structure); + return ASN1_DER_ERROR; + } + } + else + p = p->down; + } + + if ((p->type & CONST_OPTION) || (p->type & CONST_DEFAULT)) + { + p2 = _asn1_find_up (p); + len2 = strtol (p2->value, NULL, 10); + if (counter > len2) + ris = ASN1_TAG_ERROR; + } + + if (ris == ASN1_SUCCESS) + ris = + _asn1_extract_tag_der (p, der + counter, len - counter, &len2); + if (ris != ASN1_SUCCESS) + { + if (p->type & CONST_OPTION) + { + p->type |= CONST_NOT_USED; + move = RIGHT; + } + else if (p->type & CONST_DEFAULT) + { + _asn1_set_value (p, NULL, 0); + move = RIGHT; + } + else + { + if (errorDescription != NULL) + _asn1_error_description_tag_error (p, errorDescription); + + asn1_delete_structure (structure); + return ASN1_TAG_ERROR; + } + } + else + counter += len2; + } + + if (ris == ASN1_SUCCESS) + { + switch (type_field (p->type)) + { + case TYPE_NULL: + if (der[counter]) + { + asn1_delete_structure (structure); + return ASN1_DER_ERROR; + } + + if (p == nodeFound) + state = EXIT; + + counter++; + move = RIGHT; + break; + case TYPE_BOOLEAN: + if (der[counter++] != 1) + { + asn1_delete_structure (structure); + return ASN1_DER_ERROR; + } + + if (state == FOUND) + { + if (der[counter++] == 0) + _asn1_set_value (p, "F", 1); + else + _asn1_set_value (p, "T", 1); + + if (p == nodeFound) + state = EXIT; + + } + else + counter++; + + move = RIGHT; + break; + case TYPE_INTEGER: + case TYPE_ENUMERATED: + len2 = + asn1_get_length_der (der + counter, len - counter, &len3); + if (len2 < 0) + return ASN1_DER_ERROR; + if (state == FOUND) + { + if (len3 + len2 > len - counter) + return ASN1_DER_ERROR; + _asn1_set_value (p, der + counter, len3 + len2); + + if (p == nodeFound) + state = EXIT; + } + counter += len3 + len2; + move = RIGHT; + break; + case TYPE_OBJECT_ID: + if (state == FOUND) + { + _asn1_get_objectid_der (der + counter, len - counter, &len2, + temp, sizeof (temp)); + tlen = strlen (temp); + + if (tlen > 0) + _asn1_set_value (p, temp, tlen + 1); + + if (p == nodeFound) + state = EXIT; + } + else + { + len2 = + asn1_get_length_der (der + counter, len - counter, &len3); + if (len2 < 0) + return ASN1_DER_ERROR; + len2 += len3; + } + + counter += len2; + move = RIGHT; + break; + case TYPE_TIME: + if (state == FOUND) + { + result = + _asn1_get_time_der (der + counter, len - counter, &len2, + temp, sizeof (temp) - 1); + if (result != ASN1_SUCCESS) + { + asn1_delete_structure (structure); + return result; + } + + tlen = strlen (temp); + if (tlen > 0) + _asn1_set_value (p, temp, tlen + 1); + + if (p == nodeFound) + state = EXIT; + } + else + { + len2 = + asn1_get_length_der (der + counter, len - counter, &len3); + if (len2 < 0) + return ASN1_DER_ERROR; + len2 += len3; + } + + counter += len2; + move = RIGHT; + break; + case TYPE_OCTET_STRING: + len3 = len - counter; + if (state == FOUND) + { + ris = _asn1_get_octet_string (der + counter, p, &len3); + if (p == nodeFound) + state = EXIT; + } + else + ris = _asn1_get_octet_string (der + counter, NULL, &len3); + + if (ris != ASN1_SUCCESS) + return ris; + counter += len3; + move = RIGHT; + break; + case TYPE_GENERALSTRING: + len2 = + asn1_get_length_der (der + counter, len - counter, &len3); + if (len2 < 0) + return ASN1_DER_ERROR; + if (state == FOUND) + { + if (len3 + len2 > len - counter) + return ASN1_DER_ERROR; + _asn1_set_value (p, der + counter, len3 + len2); + + if (p == nodeFound) + state = EXIT; + } + counter += len3 + len2; + move = RIGHT; + break; + case TYPE_BIT_STRING: + len2 = + asn1_get_length_der (der + counter, len - counter, &len3); + if (len2 < 0) + return ASN1_DER_ERROR; + if (state == FOUND) + { + if (len3 + len2 > len - counter) + return ASN1_DER_ERROR; + _asn1_set_value (p, der + counter, len3 + len2); + + if (p == nodeFound) + state = EXIT; + } + counter += len3 + len2; + move = RIGHT; + break; + case TYPE_SEQUENCE: + case TYPE_SET: + if (move == UP) + { + len2 = strtol (p->value, NULL, 10); + _asn1_set_value (p, NULL, 0); + if (len2 == -1) + { /* indefinite length method */ + if ((der[counter]) || der[counter + 1]) + { + asn1_delete_structure (structure); + return ASN1_DER_ERROR; + } + counter += 2; + } + else + { /* definite length method */ + if (len2 != counter) + { + asn1_delete_structure (structure); + return ASN1_DER_ERROR; + } + } + if (p == nodeFound) + state = EXIT; + move = RIGHT; + } + else + { /* move==DOWN || move==RIGHT */ + if (state == OTHER_BRANCH) + { + len3 = + asn1_get_length_der (der + counter, len - counter, + &len2); + if (len3 < 0) + return ASN1_DER_ERROR; + counter += len2 + len3; + move = RIGHT; + } + else + { /* state==SAME_BRANCH or state==FOUND */ + len3 = + asn1_get_length_der (der + counter, len - counter, + &len2); + if (len3 < 0) + return ASN1_DER_ERROR; + counter += len2; + if (len3 > 0) + { + _asn1_ltostr (counter + len3, temp); + tlen = strlen (temp); + + if (tlen > 0) + _asn1_set_value (p, temp, tlen + 1); + move = DOWN; + } + else if (len3 == 0) + { + p2 = p->down; + while (p2) + { + if (type_field (p2->type) != TYPE_TAG) + { + p3 = p2->right; + asn1_delete_structure (&p2); + p2 = p3; + } + else + p2 = p2->right; + } + move = RIGHT; + } + else + { /* indefinite length method */ + _asn1_set_value (p, "-1", 3); + move = DOWN; + } + } + } + break; + case TYPE_SEQUENCE_OF: + case TYPE_SET_OF: + if (move == UP) + { + len2 = strtol (p->value, NULL, 10); + if (len2 > counter) + { + _asn1_append_sequence_set (p); + p = p->down; + while (p->right) + p = p->right; + move = RIGHT; + continue; + } + _asn1_set_value (p, NULL, 0); + if (len2 != counter) + { + asn1_delete_structure (structure); + return ASN1_DER_ERROR; + } + + if (p == nodeFound) + state = EXIT; + } + else + { /* move==DOWN || move==RIGHT */ + if (state == OTHER_BRANCH) + { + len3 = + asn1_get_length_der (der + counter, len - counter, + &len2); + if (len3 < 0) + return ASN1_DER_ERROR; + counter += len2 + len3; + move = RIGHT; + } + else + { /* state==FOUND or state==SAME_BRANCH */ + len3 = + asn1_get_length_der (der + counter, len - counter, + &len2); + if (len3 < 0) + return ASN1_DER_ERROR; + counter += len2; + if (len3) + { + _asn1_ltostr (counter + len3, temp); + tlen = strlen (temp); + + if (tlen > 0) + _asn1_set_value (p, temp, tlen + 1); + p2 = p->down; + while ((type_field (p2->type) == TYPE_TAG) + || (type_field (p2->type) == TYPE_SIZE)) + p2 = p2->right; + if (p2->right == NULL) + _asn1_append_sequence_set (p); + p = p2; + state = FOUND; + } + } + } + + break; + case TYPE_ANY: + if (asn1_get_tag_der + (der + counter, len - counter, &class, &len2, + &tag) != ASN1_SUCCESS) + return ASN1_DER_ERROR; + if (counter + len2 > len) + return ASN1_DER_ERROR; + + len4 = + asn1_get_length_der (der + counter + len2, + len - counter - len2, &len3); + if (len4 < -1) + return ASN1_DER_ERROR; + + if (len4 != -1) + { + len2 += len4; + if (state == FOUND) + { + asn1_length_der (len2 + len3, NULL, &len4); + temp2 = + (unsigned char *) _asn1_alloca (len2 + len3 + len4); + if (temp2 == NULL) + { + asn1_delete_structure (structure); + return ASN1_MEM_ALLOC_ERROR; + } + + asn1_octet_der (der + counter, len2 + len3, temp2, + &len4); + _asn1_set_value (p, temp2, len4); + _asn1_afree (temp2); + + if (p == nodeFound) + state = EXIT; + } + counter += len2 + len3; + } + else + { /* indefinite length */ + /* Check indefinite lenth method in an EXPLICIT TAG */ + if ((p->type & CONST_TAG) && (der[counter - 1] == 0x80)) + indefinite = 1; + else + indefinite = 0; + + len2 = len - counter; + ris = + _asn1_get_indefinite_length_string (der + counter, &len2); + if (ris != ASN1_SUCCESS) + { + asn1_delete_structure (structure); + return ris; + } + + if (state == FOUND) + { + asn1_length_der (len2, NULL, &len4); + temp2 = (unsigned char *) _asn1_alloca (len2 + len4); + if (temp2 == NULL) + { + asn1_delete_structure (structure); + return ASN1_MEM_ALLOC_ERROR; + } + + asn1_octet_der (der + counter, len2, temp2, &len4); + _asn1_set_value (p, temp2, len4); + _asn1_afree (temp2); + + if (p == nodeFound) + state = EXIT; + } + + counter += len2; + + /* Check if a couple of 0x00 are present due to an EXPLICIT TAG with + an indefinite length method. */ + if (indefinite) + { + if (!der[counter] && !der[counter + 1]) + { + counter += 2; + } + else + { + asn1_delete_structure (structure); + return ASN1_DER_ERROR; + } + } + } + move = RIGHT; + break; + + default: + move = (move == UP) ? RIGHT : DOWN; + break; + } + } + + if ((p == node && move != DOWN) || (state == EXIT)) + break; + + if (move == DOWN) + { + if (p->down) + { + p = p->down; + + if (state != FOUND) + { + nameLen -= strlen (p->name) + 1; + if (nameLen > 0) + { + if (currentName[0]) + strcat (currentName, "."); + strcat (currentName, p->name); + } + else + { + asn1_delete_structure (structure); + return ASN1_MEM_ERROR; + } + if (!(strcmp (currentName, elementName))) + { + state = FOUND; + nodeFound = p; + } + else + if (!memcmp + (currentName, elementName, strlen (currentName))) + state = SAME_BRANCH; + else + state = OTHER_BRANCH; + } + } + else + move = RIGHT; + } + + if ((move == RIGHT) && !(p->type & CONST_SET)) + { + if (p->right) + { + p = p->right; + + if (state != FOUND) + { + dot_p = char_p = currentName; + while ((char_p = strchr (char_p, '.'))) + { + dot_p = char_p++; + dot_p++; + } + + nameLen += strlen (currentName) - (dot_p - currentName); + *dot_p = 0; + + nameLen -= strlen (p->name); + if (nameLen > 0) + strcat (currentName, p->name); + else + { + asn1_delete_structure (structure); + return ASN1_MEM_ERROR; + } + + if (!(strcmp (currentName, elementName))) + { + state = FOUND; + nodeFound = p; + } + else + if (!memcmp + (currentName, elementName, strlen (currentName))) + state = SAME_BRANCH; + else + state = OTHER_BRANCH; + } + } + else + move = UP; + } + + if (move == UP) + { + p = _asn1_find_up (p); + + if (state != FOUND) + { + dot_p = char_p = currentName; + while ((char_p = strchr (char_p, '.'))) + { + dot_p = char_p++; + dot_p++; + } + + nameLen += strlen (currentName) - (dot_p - currentName); + *dot_p = 0; + + if (!(strcmp (currentName, elementName))) + { + state = FOUND; + nodeFound = p; + } + else + if (!memcmp (currentName, elementName, strlen (currentName))) + state = SAME_BRANCH; + else + state = OTHER_BRANCH; + } + } + } + + _asn1_delete_not_used (*structure); + + if (counter > len) + { + asn1_delete_structure (structure); + return ASN1_DER_ERROR; + } + + return ASN1_SUCCESS; +} + + + +/** + * asn1_der_decoding_startEnd - Find the start and end point of an element in a DER encoding string. + * @element: pointer to an ASN1 element + * @ider: vector that contains the DER encoding. + * @len: number of bytes of *@ider: @ider[0]..@ider[len-1] + * @name_element: an element of NAME structure. + * @start: the position of the first byte of NAME_ELEMENT decoding + * (@ider[*start]) + * @end: the position of the last byte of NAME_ELEMENT decoding + * (@ider[*end]) + * + * Find the start and end point of an element in a DER encoding + * string. I mean that if you have a der encoding and you have + * already used the function "asn1_der_decoding" to fill a structure, + * it may happen that you want to find the piece of string concerning + * an element of the structure. + * + * Example: the sequence "tbsCertificate" inside an X509 certificate. + * + * Returns: + * + * ASN1_SUCCESS: DER encoding OK. + * + * ASN1_ELEMENT_NOT_FOUND: ELEMENT is ASN1_TYPE EMPTY or + * NAME_ELEMENT is not a valid element. + * + * ASN1_TAG_ERROR,ASN1_DER_ERROR: the der encoding doesn't match + * the structure ELEMENT. + * + **/ +asn1_retCode +asn1_der_decoding_startEnd (ASN1_TYPE element, const void *ider, int len, + const char *name_element, int *start, int *end) +{ + node_asn *node, *node_to_find, *p, *p2, *p3; + int counter, len2, len3, len4, move, ris; + unsigned char class; + unsigned long tag; + int indefinite; + const unsigned char *der = ider; + + node = element; + + if (node == ASN1_TYPE_EMPTY) + return ASN1_ELEMENT_NOT_FOUND; + + node_to_find = asn1_find_node (node, name_element); + + if (node_to_find == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + if (node_to_find == node) + { + *start = 0; + *end = len - 1; + return ASN1_SUCCESS; + } + + if (node->type & CONST_OPTION) + return ASN1_GENERIC_ERROR; + + counter = 0; + move = DOWN; + p = node; + while (1) + { + ris = ASN1_SUCCESS; + + if (move != UP) + { + if (p->type & CONST_SET) + { + p2 = _asn1_find_up (p); + len2 = strtol (p2->value, NULL, 10); + if (len2 == -1) + { + if (!der[counter] && !der[counter + 1]) + { + p = p2; + move = UP; + counter += 2; + continue; + } + } + else if (counter == len2) + { + p = p2; + move = UP; + continue; + } + else if (counter > len2) + return ASN1_DER_ERROR; + p2 = p2->down; + while (p2) + { + if ((p2->type & CONST_SET) && (p2->type & CONST_NOT_USED)) + { /* CONTROLLARE */ + if (type_field (p2->type) != TYPE_CHOICE) + ris = + _asn1_extract_tag_der (p2, der + counter, + len - counter, &len2); + else + { + p3 = p2->down; + ris = + _asn1_extract_tag_der (p3, der + counter, + len - counter, &len2); + } + if (ris == ASN1_SUCCESS) + { + p2->type &= ~CONST_NOT_USED; + p = p2; + break; + } + } + p2 = p2->right; + } + if (p2 == NULL) + return ASN1_DER_ERROR; + } + + if (p == node_to_find) + *start = counter; + + if (type_field (p->type) == TYPE_CHOICE) + { + p = p->down; + ris = + _asn1_extract_tag_der (p, der + counter, len - counter, + &len2); + if (p == node_to_find) + *start = counter; + } + + if (ris == ASN1_SUCCESS) + ris = + _asn1_extract_tag_der (p, der + counter, len - counter, &len2); + if (ris != ASN1_SUCCESS) + { + if (p->type & CONST_OPTION) + { + p->type |= CONST_NOT_USED; + move = RIGHT; + } + else if (p->type & CONST_DEFAULT) + { + move = RIGHT; + } + else + { + return ASN1_TAG_ERROR; + } + } + else + counter += len2; + } + + if (ris == ASN1_SUCCESS) + { + switch (type_field (p->type)) + { + case TYPE_NULL: + if (der[counter]) + return ASN1_DER_ERROR; + counter++; + move = RIGHT; + break; + case TYPE_BOOLEAN: + if (der[counter++] != 1) + return ASN1_DER_ERROR; + counter++; + move = RIGHT; + break; + case TYPE_INTEGER: + case TYPE_ENUMERATED: + len2 = + asn1_get_length_der (der + counter, len - counter, &len3); + if (len2 < 0) + return ASN1_DER_ERROR; + counter += len3 + len2; + move = RIGHT; + break; + case TYPE_OBJECT_ID: + len2 = + asn1_get_length_der (der + counter, len - counter, &len3); + if (len2 < 0) + return ASN1_DER_ERROR; + counter += len2 + len3; + move = RIGHT; + break; + case TYPE_TIME: + len2 = + asn1_get_length_der (der + counter, len - counter, &len3); + if (len2 < 0) + return ASN1_DER_ERROR; + counter += len2 + len3; + move = RIGHT; + break; + case TYPE_OCTET_STRING: + len3 = len - counter; + ris = _asn1_get_octet_string (der + counter, NULL, &len3); + if (ris != ASN1_SUCCESS) + return ris; + counter += len3; + move = RIGHT; + break; + case TYPE_GENERALSTRING: + len2 = + asn1_get_length_der (der + counter, len - counter, &len3); + if (len2 < 0) + return ASN1_DER_ERROR; + counter += len3 + len2; + move = RIGHT; + break; + case TYPE_BIT_STRING: + len2 = + asn1_get_length_der (der + counter, len - counter, &len3); + if (len2 < 0) + return ASN1_DER_ERROR; + counter += len3 + len2; + move = RIGHT; + break; + case TYPE_SEQUENCE: + case TYPE_SET: + if (move != UP) + { + len3 = + asn1_get_length_der (der + counter, len - counter, &len2); + if (len3 < -1) + return ASN1_DER_ERROR; + counter += len2; + if (len3 == 0) + move = RIGHT; + else + move = DOWN; + } + else + { + if (!der[counter] && !der[counter + 1]) /* indefinite length method */ + counter += 2; + move = RIGHT; + } + break; + case TYPE_SEQUENCE_OF: + case TYPE_SET_OF: + if (move != UP) + { + len3 = + asn1_get_length_der (der + counter, len - counter, &len2); + if (len3 < -1) + return ASN1_DER_ERROR; + counter += len2; + if ((len3 == -1) && !der[counter] && !der[counter + 1]) + counter += 2; + else if (len3) + { + p2 = p->down; + while ((type_field (p2->type) == TYPE_TAG) || + (type_field (p2->type) == TYPE_SIZE)) + p2 = p2->right; + p = p2; + } + } + else + { + if (!der[counter] && !der[counter + 1]) /* indefinite length method */ + counter += 2; + } + move = RIGHT; + break; + case TYPE_ANY: + if (asn1_get_tag_der + (der + counter, len - counter, &class, &len2, + &tag) != ASN1_SUCCESS) + return ASN1_DER_ERROR; + if (counter + len2 > len) + return ASN1_DER_ERROR; + + len4 = + asn1_get_length_der (der + counter + len2, + len - counter - len2, &len3); + if (len4 < -1) + return ASN1_DER_ERROR; + + if (len4 != -1) + { + counter += len2 + len4 + len3; + } + else + { /* indefinite length */ + /* Check indefinite lenth method in an EXPLICIT TAG */ + if ((p->type & CONST_TAG) && (der[counter - 1] == 0x80)) + indefinite = 1; + else + indefinite = 0; + + len2 = len - counter; + ris = + _asn1_get_indefinite_length_string (der + counter, &len2); + if (ris != ASN1_SUCCESS) + return ris; + counter += len2; + + /* Check if a couple of 0x00 are present due to an EXPLICIT TAG with + an indefinite length method. */ + if (indefinite) + { + if (!der[counter] && !der[counter + 1]) + counter += 2; + else + return ASN1_DER_ERROR; + } + } + move = RIGHT; + break; + default: + move = (move == UP) ? RIGHT : DOWN; + break; + } + } + + if ((p == node_to_find) && (move == RIGHT)) + { + *end = counter - 1; + return ASN1_SUCCESS; + } + + if (p == node && move != DOWN) + break; + + if (move == DOWN) + { + if (p->down) + p = p->down; + else + move = RIGHT; + } + if ((move == RIGHT) && !(p->type & CONST_SET)) + { + if (p->right) + p = p->right; + else + move = UP; + } + if (move == UP) + p = _asn1_find_up (p); + } + + return ASN1_ELEMENT_NOT_FOUND; +} + + +/** + * asn1_expand_any_defined_by - Expand "ANY DEFINED BY" fields in structure. + * @definitions: ASN1 definitions + * @element: pointer to an ASN1 structure + * + * Expands every "ANY DEFINED BY" element of a structure created from + * a DER decoding process (asn1_der_decoding function). The element ANY + * must be defined by an OBJECT IDENTIFIER. The type used to expand + * the element ANY is the first one following the definition of + * the actual value of the OBJECT IDENTIFIER. + * + * + * Returns: + * + * ASN1_SUCCESS: Substitution OK. + * + * ASN1_ERROR_TYPE_ANY: Some "ANY DEFINED BY" element couldn't be + * expanded due to a problem in OBJECT_ID -> TYPE association. + * + * other errors: Result of der decoding process. + **/ + +asn1_retCode +asn1_expand_any_defined_by (ASN1_TYPE definitions, ASN1_TYPE * element) +{ + char definitionsName[MAX_NAME_SIZE], name[2 * MAX_NAME_SIZE + 1], + value[MAX_NAME_SIZE]; + asn1_retCode retCode = ASN1_SUCCESS, result; + int len, len2, len3; + ASN1_TYPE p, p2, p3, aux = ASN1_TYPE_EMPTY; + char errorDescription[MAX_ERROR_DESCRIPTION_SIZE]; + + if ((definitions == ASN1_TYPE_EMPTY) || (*element == ASN1_TYPE_EMPTY)) + return ASN1_ELEMENT_NOT_FOUND; + + strcpy (definitionsName, definitions->name); + strcat (definitionsName, "."); + + p = *element; + while (p) + { + + switch (type_field (p->type)) + { + case TYPE_ANY: + if ((p->type & CONST_DEFINED_BY) && (p->value)) + { + /* search the "DEF_BY" element */ + p2 = p->down; + while ((p2) && (type_field (p2->type) != TYPE_CONSTANT)) + p2 = p2->right; + + if (!p2) + { + retCode = ASN1_ERROR_TYPE_ANY; + break; + } + + p3 = _asn1_find_up (p); + + if (!p3) + { + retCode = ASN1_ERROR_TYPE_ANY; + break; + } + + p3 = p3->down; + while (p3) + { + if ((p3->name) && !(strcmp (p3->name, p2->name))) + break; + p3 = p3->right; + } + + if ((!p3) || (type_field (p3->type) != TYPE_OBJECT_ID) || + (p3->value == NULL)) + { + + p3 = _asn1_find_up (p); + p3 = _asn1_find_up (p3); + + if (!p3) + { + retCode = ASN1_ERROR_TYPE_ANY; + break; + } + + p3 = p3->down; + + while (p3) + { + if ((p3->name) && !(strcmp (p3->name, p2->name))) + break; + p3 = p3->right; + } + + if ((!p3) || (type_field (p3->type) != TYPE_OBJECT_ID) || + (p3->value == NULL)) + { + retCode = ASN1_ERROR_TYPE_ANY; + break; + } + } + + /* search the OBJECT_ID into definitions */ + p2 = definitions->down; + while (p2) + { + if ((type_field (p2->type) == TYPE_OBJECT_ID) && + (p2->type & CONST_ASSIGN)) + { + strcpy (name, definitionsName); + strcat (name, p2->name); + + len = MAX_NAME_SIZE; + result = + asn1_read_value (definitions, name, value, &len); + + if ((result == ASN1_SUCCESS) + && (!strcmp (p3->value, value))) + { + p2 = p2->right; /* pointer to the structure to + use for expansion */ + while ((p2) && (p2->type & CONST_ASSIGN)) + p2 = p2->right; + + if (p2) + { + strcpy (name, definitionsName); + strcat (name, p2->name); + + result = + asn1_create_element (definitions, name, &aux); + if (result == ASN1_SUCCESS) + { + _asn1_set_name (aux, p->name); + len2 = + asn1_get_length_der (p->value, + p->value_len, &len3); + if (len2 < 0) + return ASN1_DER_ERROR; + + result = + asn1_der_decoding (&aux, p->value + len3, + len2, + errorDescription); + if (result == ASN1_SUCCESS) + { + + _asn1_set_right (aux, p->right); + _asn1_set_right (p, aux); + + result = asn1_delete_structure (&p); + if (result == ASN1_SUCCESS) + { + p = aux; + aux = ASN1_TYPE_EMPTY; + break; + } + else + { /* error with asn1_delete_structure */ + asn1_delete_structure (&aux); + retCode = result; + break; + } + } + else + { /* error with asn1_der_decoding */ + retCode = result; + break; + } + } + else + { /* error with asn1_create_element */ + retCode = result; + break; + } + } + else + { /* error with the pointer to the structure to exapand */ + retCode = ASN1_ERROR_TYPE_ANY; + break; + } + } + } + p2 = p2->right; + } /* end while */ + + if (!p2) + { + retCode = ASN1_ERROR_TYPE_ANY; + break; + } + + } + break; + default: + break; + } + + + if (p->down) + { + p = p->down; + } + else if (p == *element) + { + p = NULL; + break; + } + else if (p->right) + p = p->right; + else + { + while (1) + { + p = _asn1_find_up (p); + if (p == *element) + { + p = NULL; + break; + } + if (p->right) + { + p = p->right; + break; + } + } + } + } + + return retCode; +} + + + +/** + * asn1_expand_octet_string - Expand "OCTET STRING" fields in structure. + * @definitions: ASN1 definitions + * @element: pointer to an ASN1 structure + * @octetName: name of the OCTECT STRING field to expand. + * @objectName: name of the OBJECT IDENTIFIER field to use to define + * the type for expansion. + * + * Expands an "OCTET STRING" element of a structure created from a + * DER decoding process (asn1_der_decoding function). The type used + * for expansion is the first one following the definition of the + * actual value of the OBJECT IDENTIFIER indicated by OBJECTNAME. + * + * Returns: + * + * ASN1_SUCCESS: Substitution OK. + * + * ASN1_ELEMENT_NOT_FOUND: OBJECTNAME or OCTETNAME are not correct. + * + * ASN1_VALUE_NOT_VALID: Wasn't possible to find the type to use + * for expansion. + * + * other errors: result of der decoding process. + **/ +asn1_retCode +asn1_expand_octet_string (ASN1_TYPE definitions, ASN1_TYPE * element, + const char *octetName, const char *objectName) +{ + char name[2 * MAX_NAME_SIZE + 1], value[MAX_NAME_SIZE]; + asn1_retCode retCode = ASN1_SUCCESS, result; + int len, len2, len3; + ASN1_TYPE p2, aux = ASN1_TYPE_EMPTY; + ASN1_TYPE octetNode = ASN1_TYPE_EMPTY, objectNode = ASN1_TYPE_EMPTY; + char errorDescription[MAX_ERROR_DESCRIPTION_SIZE]; + + if ((definitions == ASN1_TYPE_EMPTY) || (*element == ASN1_TYPE_EMPTY)) + return ASN1_ELEMENT_NOT_FOUND; + + octetNode = asn1_find_node (*element, octetName); + if (octetNode == ASN1_TYPE_EMPTY) + return ASN1_ELEMENT_NOT_FOUND; + if (type_field (octetNode->type) != TYPE_OCTET_STRING) + return ASN1_ELEMENT_NOT_FOUND; + if (octetNode->value == NULL) + return ASN1_VALUE_NOT_FOUND; + + objectNode = asn1_find_node (*element, objectName); + if (objectNode == ASN1_TYPE_EMPTY) + return ASN1_ELEMENT_NOT_FOUND; + + if (type_field (objectNode->type) != TYPE_OBJECT_ID) + return ASN1_ELEMENT_NOT_FOUND; + + if (objectNode->value == NULL) + return ASN1_VALUE_NOT_FOUND; + + + /* search the OBJECT_ID into definitions */ + p2 = definitions->down; + while (p2) + { + if ((type_field (p2->type) == TYPE_OBJECT_ID) && + (p2->type & CONST_ASSIGN)) + { + strcpy (name, definitions->name); + strcat (name, "."); + strcat (name, p2->name); + + len = sizeof (value); + result = asn1_read_value (definitions, name, value, &len); + + if ((result == ASN1_SUCCESS) + && (!strcmp (objectNode->value, value))) + { + + p2 = p2->right; /* pointer to the structure to + use for expansion */ + while ((p2) && (p2->type & CONST_ASSIGN)) + p2 = p2->right; + + if (p2) + { + strcpy (name, definitions->name); + strcat (name, "."); + strcat (name, p2->name); + + result = asn1_create_element (definitions, name, &aux); + if (result == ASN1_SUCCESS) + { + _asn1_set_name (aux, octetNode->name); + len2 = + asn1_get_length_der (octetNode->value, + octetNode->value_len, &len3); + if (len2 < 0) + return ASN1_DER_ERROR; + + result = + asn1_der_decoding (&aux, octetNode->value + len3, + len2, errorDescription); + if (result == ASN1_SUCCESS) + { + + _asn1_set_right (aux, octetNode->right); + _asn1_set_right (octetNode, aux); + + result = asn1_delete_structure (&octetNode); + if (result == ASN1_SUCCESS) + { + aux = ASN1_TYPE_EMPTY; + break; + } + else + { /* error with asn1_delete_structure */ + asn1_delete_structure (&aux); + retCode = result; + break; + } + } + else + { /* error with asn1_der_decoding */ + retCode = result; + break; + } + } + else + { /* error with asn1_create_element */ + retCode = result; + break; + } + } + else + { /* error with the pointer to the structure to exapand */ + retCode = ASN1_VALUE_NOT_VALID; + break; + } + } + } + + p2 = p2->right; + + } + + if (!p2) + retCode = ASN1_VALUE_NOT_VALID; + + return retCode; +} diff --git a/src/daemon/https/minitasn1/element.c b/src/daemon/https/minitasn1/element.c new file mode 100644 index 00000000..25b4975a --- /dev/null +++ b/src/daemon/https/minitasn1/element.c @@ -0,0 +1,1013 @@ +/* + * Copyright (C) 2004, 2006 Free Software Foundation + * Copyright (C) 2000, 2001, 2002, 2003 Fabio Fiorina + * + * This file is part of LIBTASN1. + * + * The LIBTASN1 library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +/*****************************************************/ +/* File: element.c */ +/* Description: Functions with the read and write */ +/* functions. */ +/*****************************************************/ + + +#include +#include +#include "parser_aux.h" +#include +#include "structure.h" + +void +_asn1_hierarchical_name (node_asn * node, char *name, int name_size) +{ + node_asn *p; + char tmp_name[64]; + + p = node; + + name[0] = 0; + + while (p != NULL) + { + if (p->name != NULL) + { + _asn1_str_cpy (tmp_name, sizeof (tmp_name), name), + _asn1_str_cpy (name, name_size, p->name); + _asn1_str_cat (name, name_size, "."); + _asn1_str_cat (name, name_size, tmp_name); + } + p = _asn1_find_up (p); + } + + if (name[0] == 0) + _asn1_str_cpy (name, name_size, "ROOT"); +} + + +/******************************************************************/ +/* Function : _asn1_convert_integer */ +/* Description: converts an integer from a null terminated string */ +/* to der decoding. The convertion from a null */ +/* terminated string to an integer is made with */ +/* the 'strtol' function. */ +/* Parameters: */ +/* value: null terminated string to convert. */ +/* value_out: convertion result (memory must be already */ +/* allocated). */ +/* value_out_size: number of bytes of value_out. */ +/* len: number of significant byte of value_out. */ +/* Return: ASN1_MEM_ERROR or ASN1_SUCCESS */ +/******************************************************************/ +asn1_retCode +_asn1_convert_integer (const char *value, unsigned char *value_out, + int value_out_size, int *len) +{ + char negative; + unsigned char val[SIZEOF_UNSIGNED_LONG_INT]; + long valtmp; + int k, k2; + + valtmp = strtol (value, NULL, 10); + + for (k = 0; k < SIZEOF_UNSIGNED_LONG_INT; k++) + { + val[SIZEOF_UNSIGNED_LONG_INT - k - 1] = (valtmp >> (8 * k)) & 0xFF; + } + + if (val[0] & 0x80) + negative = 1; + else + negative = 0; + + for (k = 0; k < SIZEOF_UNSIGNED_LONG_INT - 1; k++) + { + if (negative && (val[k] != 0xFF)) + break; + else if (!negative && val[k]) + break; + } + + if ((negative && !(val[k] & 0x80)) || (!negative && (val[k] & 0x80))) + k--; + + *len = SIZEOF_UNSIGNED_LONG_INT - k; + + if (SIZEOF_UNSIGNED_LONG_INT - k > value_out_size) + /* VALUE_OUT is too short to contain the value conversion */ + return ASN1_MEM_ERROR; + + for (k2 = k; k2 < SIZEOF_UNSIGNED_LONG_INT; k2++) + value_out[k2 - k] = val[k2]; + + +#ifdef LIBTASN1_DEBUG_INTEGER + _libtasn1_log ("_asn1_convert_integer: valueIn=%s, lenOut=%d", value, *len); + for (k = 0; k < SIZEOF_UNSIGNED_LONG_INT; k++) + _libtasn1_log (", vOut[%d]=%d", k, value_out[k]); + _libtasn1_log ("\n"); +#endif + + + return ASN1_SUCCESS; +} + + +int +_asn1_append_sequence_set (node_asn * node) +{ + node_asn *p, *p2; + char temp[10]; + long n; + + if (!node || !(node->down)) + return ASN1_GENERIC_ERROR; + + p = node->down; + while ((type_field (p->type) == TYPE_TAG) + || (type_field (p->type) == TYPE_SIZE)) + p = p->right; + p2 = _asn1_copy_structure3 (p); + while (p->right) + p = p->right; + _asn1_set_right (p, p2); + + if (p->name == NULL) + _asn1_str_cpy (temp, sizeof (temp), "?1"); + else + { + n = strtol (p->name + 1, NULL, 0); + n++; + temp[0] = '?'; + _asn1_ltostr (n, temp + 1); + } + _asn1_set_name (p2, temp); + /* p2->type |= CONST_OPTION; */ + + return ASN1_SUCCESS; +} + + +/** + * asn1_write_value - Set the value of one element inside a structure. + * @node_root: pointer to a structure + * @name: the name of the element inside the structure that you want to set. + * @ivalue: vector used to specify the value to set. If len is >0, + * VALUE must be a two's complement form integer. if len=0 *VALUE + * must be a null terminated string with an integer value. + * @len: number of bytes of *value to use to set the value: + * value[0]..value[len-1] or 0 if value is a null terminated string + * + * Set the value of one element inside a structure. + * + * If an element is OPTIONAL and you want to delete it, you must use + * the value=NULL and len=0. Using "pkix.asn": + * + * result=asn1_write_value(cert, "tbsCertificate.issuerUniqueID", + * NULL, 0); + * + * Description for each type: + * + * INTEGER: VALUE must contain a two's complement form integer. + * + * value[0]=0xFF , len=1 -> integer=-1. + * value[0]=0xFF value[1]=0xFF , len=2 -> integer=-1. + * value[0]=0x01 , len=1 -> integer= 1. + * value[0]=0x00 value[1]=0x01 , len=2 -> integer= 1. + * value="123" , len=0 -> integer= 123. + * + * ENUMERATED: As INTEGER (but only with not negative numbers). + * + * BOOLEAN: VALUE must be the null terminated string "TRUE" or + * "FALSE" and LEN != 0. + * + * value="TRUE" , len=1 -> boolean=TRUE. + * value="FALSE" , len=1 -> boolean=FALSE. + * + * OBJECT IDENTIFIER: VALUE must be a null terminated string with + * each number separated by a dot (e.g. "1.2.3.543.1"). LEN != 0. + * + * value="1 2 840 10040 4 3" , len=1 -> OID=dsa-with-sha. + * + * UTCTime: VALUE must be a null terminated string in one of these + * formats: "YYMMDDhhmmssZ", "YYMMDDhhmmssZ", + * "YYMMDDhhmmss+hh'mm'", "YYMMDDhhmmss-hh'mm'", + * "YYMMDDhhmm+hh'mm'", or "YYMMDDhhmm-hh'mm'". LEN != 0. + * + * value="9801011200Z" , len=1 -> time=Jannuary 1st, 1998 + * at 12h 00m Greenwich Mean Time + * + * GeneralizedTime: VALUE must be in one of this format: + * "YYYYMMDDhhmmss.sZ", "YYYYMMDDhhmmss.sZ", + * "YYYYMMDDhhmmss.s+hh'mm'", "YYYYMMDDhhmmss.s-hh'mm'", + * "YYYYMMDDhhmm+hh'mm'", or "YYYYMMDDhhmm-hh'mm'" where ss.s + * indicates the seconds with any precision like "10.1" or "01.02". + * LEN != 0 + * + * value="2001010112001.12-0700" , len=1 -> time=Jannuary + * 1st, 2001 at 12h 00m 01.12s Pacific Daylight Time + * + * OCTET STRING: VALUE contains the octet string and LEN is the + * number of octets. + * + * value="$\backslash$x01$\backslash$x02$\backslash$x03" , + * len=3 -> three bytes octet string + * + * GeneralString: VALUE contains the generalstring and LEN is the + * number of octets. + * + * value="$\backslash$x01$\backslash$x02$\backslash$x03" , + * len=3 -> three bytes generalstring + * + * BIT STRING: VALUE contains the bit string organized by bytes and + * LEN is the number of bits. + * + * value="$\backslash$xCF" , len=6 -> bit string="110011" (six + * bits) + * + * CHOICE: if NAME indicates a choice type, VALUE must specify one of + * the alternatives with a null terminated string. LEN != 0. Using + * "pkix.asn"\: + * + * result=asn1_write_value(cert, + * "certificate1.tbsCertificate.subject", "rdnSequence", + * 1); + * + * ANY: VALUE indicates the der encoding of a structure. LEN != 0. + * + * SEQUENCE OF: VALUE must be the null terminated string "NEW" and + * LEN != 0. With this instruction another element is appended in + * the sequence. The name of this element will be "?1" if it's the + * first one, "?2" for the second and so on. + * + * Using "pkix.asn"\: + * + * result=asn1_write_value(cert, + * "certificate1.tbsCertificate.subject.rdnSequence", "NEW", 1); + * + * SET OF: the same as SEQUENCE OF. Using "pkix.asn": + * + * result=asn1_write_value(cert, + * "tbsCertificate.subject.rdnSequence.?LAST", "NEW", 1); + * + * Returns: + * + * ASN1_SUCCESS: Set value OK. + * + * ASN1_ELEMENT_NOT_FOUND: NAME is not a valid element. + * + * ASN1_VALUE_NOT_VALID: VALUE has a wrong format. + * + **/ +asn1_retCode +asn1_write_value (ASN1_TYPE node_root, const char *name, + const void *ivalue, int len) +{ + node_asn *node, *p, *p2; + unsigned char *temp, *value_temp = NULL, *default_temp = NULL; + int len2, k, k2, negative; + const unsigned char *value = ivalue; + + node = asn1_find_node (node_root, name); + if (node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + if ((node->type & CONST_OPTION) && (value == NULL) && (len == 0)) + { + asn1_delete_structure (&node); + return ASN1_SUCCESS; + } + + if ((type_field (node->type) == TYPE_SEQUENCE_OF) && (value == NULL) + && (len == 0)) + { + p = node->down; + while ((type_field (p->type) == TYPE_TAG) + || (type_field (p->type) == TYPE_SIZE)) + p = p->right; + + while (p->right) + asn1_delete_structure (&p->right); + + return ASN1_SUCCESS; + } + + switch (type_field (node->type)) + { + case TYPE_BOOLEAN: + if (!strcmp (value, "TRUE")) + { + if (node->type & CONST_DEFAULT) + { + p = node->down; + while (type_field (p->type) != TYPE_DEFAULT) + p = p->right; + if (p->type & CONST_TRUE) + _asn1_set_value (node, NULL, 0); + else + _asn1_set_value (node, "T", 1); + } + else + _asn1_set_value (node, "T", 1); + } + else if (!strcmp (value, "FALSE")) + { + if (node->type & CONST_DEFAULT) + { + p = node->down; + while (type_field (p->type) != TYPE_DEFAULT) + p = p->right; + if (p->type & CONST_FALSE) + _asn1_set_value (node, NULL, 0); + else + _asn1_set_value (node, "F", 1); + } + else + _asn1_set_value (node, "F", 1); + } + else + return ASN1_VALUE_NOT_VALID; + break; + case TYPE_INTEGER: + case TYPE_ENUMERATED: + if (len == 0) + { + if ((isdigit (value[0])) || (value[0] == '-')) + { + value_temp = + (unsigned char *) _asn1_alloca (SIZEOF_UNSIGNED_LONG_INT); + if (value_temp == NULL) + return ASN1_MEM_ALLOC_ERROR; + + _asn1_convert_integer (value, value_temp, + SIZEOF_UNSIGNED_LONG_INT, &len); + } + else + { /* is an identifier like v1 */ + if (!(node->type & CONST_LIST)) + return ASN1_VALUE_NOT_VALID; + p = node->down; + while (p) + { + if (type_field (p->type) == TYPE_CONSTANT) + { + if ((p->name) && (!strcmp (p->name, value))) + { + value_temp = + (unsigned char *) + _asn1_alloca (SIZEOF_UNSIGNED_LONG_INT); + if (value_temp == NULL) + return ASN1_MEM_ALLOC_ERROR; + + _asn1_convert_integer (p->value, + value_temp, + SIZEOF_UNSIGNED_LONG_INT, + &len); + break; + } + } + p = p->right; + } + if (p == NULL) + return ASN1_VALUE_NOT_VALID; + } + } + else + { /* len != 0 */ + value_temp = (unsigned char *) _asn1_alloca (len); + if (value_temp == NULL) + return ASN1_MEM_ALLOC_ERROR; + memcpy (value_temp, value, len); + } + + + if (value_temp[0] & 0x80) + negative = 1; + else + negative = 0; + + if (negative && (type_field (node->type) == TYPE_ENUMERATED)) + { + _asn1_afree (value_temp); + return ASN1_VALUE_NOT_VALID; + } + + for (k = 0; k < len - 1; k++) + if (negative && (value_temp[k] != 0xFF)) + break; + else if (!negative && value_temp[k]) + break; + + if ((negative && !(value_temp[k] & 0x80)) || + (!negative && (value_temp[k] & 0x80))) + k--; + + asn1_length_der (len - k, NULL, &len2); + temp = (unsigned char *) _asn1_alloca (len - k + len2); + if (temp == NULL) + return ASN1_MEM_ALLOC_ERROR; + + asn1_octet_der (value_temp + k, len - k, temp, &len2); + _asn1_set_value (node, temp, len2); + + _asn1_afree (temp); + + + if (node->type & CONST_DEFAULT) + { + p = node->down; + while (type_field (p->type) != TYPE_DEFAULT) + p = p->right; + if ((isdigit (p->value[0])) || (p->value[0] == '-')) + { + default_temp = + (unsigned char *) _asn1_alloca (SIZEOF_UNSIGNED_LONG_INT); + if (default_temp == NULL) + return ASN1_MEM_ALLOC_ERROR; + + _asn1_convert_integer (p->value, default_temp, + SIZEOF_UNSIGNED_LONG_INT, &len2); + } + else + { /* is an identifier like v1 */ + if (!(node->type & CONST_LIST)) + return ASN1_VALUE_NOT_VALID; + p2 = node->down; + while (p2) + { + if (type_field (p2->type) == TYPE_CONSTANT) + { + if ((p2->name) && (!strcmp (p2->name, p->value))) + { + default_temp = + (unsigned char *) + _asn1_alloca (SIZEOF_UNSIGNED_LONG_INT); + if (default_temp == NULL) + return ASN1_MEM_ALLOC_ERROR; + + _asn1_convert_integer (p2->value, + default_temp, + SIZEOF_UNSIGNED_LONG_INT, + &len2); + break; + } + } + p2 = p2->right; + } + if (p2 == NULL) + return ASN1_VALUE_NOT_VALID; + } + + + if ((len - k) == len2) + { + for (k2 = 0; k2 < len2; k2++) + if (value_temp[k + k2] != default_temp[k2]) + { + break; + } + if (k2 == len2) + _asn1_set_value (node, NULL, 0); + } + _asn1_afree (default_temp); + } + _asn1_afree (value_temp); + break; + case TYPE_OBJECT_ID: + for (k = 0; k < strlen (value); k++) + if ((!isdigit (value[k])) && (value[k] != '.') && (value[k] != '+')) + return ASN1_VALUE_NOT_VALID; + if (node->type & CONST_DEFAULT) + { + p = node->down; + while (type_field (p->type) != TYPE_DEFAULT) + p = p->right; + if (!strcmp (value, p->value)) + { + _asn1_set_value (node, NULL, 0); + break; + } + } + _asn1_set_value (node, value, strlen (value) + 1); + break; + case TYPE_TIME: + if (node->type & CONST_UTC) + { + if (strlen (value) < 11) + return ASN1_VALUE_NOT_VALID; + for (k = 0; k < 10; k++) + if (!isdigit (value[k])) + return ASN1_VALUE_NOT_VALID; + switch (strlen (value)) + { + case 11: + if (value[10] != 'Z') + return ASN1_VALUE_NOT_VALID; + break; + case 13: + if ((!isdigit (value[10])) || (!isdigit (value[11])) || + (value[12] != 'Z')) + return ASN1_VALUE_NOT_VALID; + break; + case 15: + if ((value[10] != '+') && (value[10] != '-')) + return ASN1_VALUE_NOT_VALID; + for (k = 11; k < 15; k++) + if (!isdigit (value[k])) + return ASN1_VALUE_NOT_VALID; + break; + case 17: + if ((!isdigit (value[10])) || (!isdigit (value[11]))) + return ASN1_VALUE_NOT_VALID; + if ((value[12] != '+') && (value[12] != '-')) + return ASN1_VALUE_NOT_VALID; + for (k = 13; k < 17; k++) + if (!isdigit (value[k])) + return ASN1_VALUE_NOT_VALID; + break; + default: + return ASN1_VALUE_NOT_FOUND; + } + _asn1_set_value (node, value, strlen (value) + 1); + } + else + { /* GENERALIZED TIME */ + if (value) + _asn1_set_value (node, value, strlen (value) + 1); + } + break; + case TYPE_OCTET_STRING: + if (len == 0) + len = strlen (value); + asn1_length_der (len, NULL, &len2); + temp = (unsigned char *) _asn1_alloca (len + len2); + if (temp == NULL) + return ASN1_MEM_ALLOC_ERROR; + + asn1_octet_der (value, len, temp, &len2); + _asn1_set_value (node, temp, len2); + _asn1_afree (temp); + break; + case TYPE_GENERALSTRING: + if (len == 0) + len = strlen (value); + asn1_length_der (len, NULL, &len2); + temp = (unsigned char *) _asn1_alloca (len + len2); + if (temp == NULL) + return ASN1_MEM_ALLOC_ERROR; + + asn1_octet_der (value, len, temp, &len2); + _asn1_set_value (node, temp, len2); + _asn1_afree (temp); + break; + case TYPE_BIT_STRING: + if (len == 0) + len = strlen (value); + asn1_length_der ((len >> 3) + 2, NULL, &len2); + temp = (unsigned char *) _asn1_alloca ((len >> 3) + 2 + len2); + if (temp == NULL) + return ASN1_MEM_ALLOC_ERROR; + + asn1_bit_der (value, len, temp, &len2); + _asn1_set_value (node, temp, len2); + _asn1_afree (temp); + break; + case TYPE_CHOICE: + p = node->down; + while (p) + { + if (!strcmp (p->name, value)) + { + p2 = node->down; + while (p2) + { + if (p2 != p) + { + asn1_delete_structure (&p2); + p2 = node->down; + } + else + p2 = p2->right; + } + break; + } + p = p->right; + } + if (!p) + return ASN1_ELEMENT_NOT_FOUND; + break; + case TYPE_ANY: + asn1_length_der (len, NULL, &len2); + temp = (unsigned char *) _asn1_alloca (len + len2); + if (temp == NULL) + return ASN1_MEM_ALLOC_ERROR; + + asn1_octet_der (value, len, temp, &len2); + _asn1_set_value (node, temp, len2); + _asn1_afree (temp); + break; + case TYPE_SEQUENCE_OF: + case TYPE_SET_OF: + if (strcmp (value, "NEW")) + return ASN1_VALUE_NOT_VALID; + _asn1_append_sequence_set (node); + break; + default: + return ASN1_ELEMENT_NOT_FOUND; + break; + } + + return ASN1_SUCCESS; +} + + +#define PUT_VALUE( ptr, ptr_size, data, data_size) \ + *len = data_size; \ + if (ptr_size < data_size) { \ + return ASN1_MEM_ERROR; \ + } else { \ + memcpy( ptr, data, data_size); \ + } + +#define PUT_STR_VALUE( ptr, ptr_size, data) \ + *len = strlen(data) + 1; \ + if (ptr_size < *len) { \ + return ASN1_MEM_ERROR; \ + } else { \ + /* this strcpy is checked */ \ + strcpy(ptr, data); \ + } + +#define ADD_STR_VALUE( ptr, ptr_size, data) \ + *len = strlen(data) + 1; \ + if (ptr_size < strlen(ptr)+(*len)) { \ + return ASN1_MEM_ERROR; \ + } else { \ + /* this strcat is checked */ \ + strcat(ptr, data); \ + } + +/** + * asn1_read_value - Returns the value of one element inside a structure + * @root: pointer to a structure. + * @name: the name of the element inside a structure that you want to read. + * @ivalue: vector that will contain the element's content, must be a + * pointer to memory cells already allocated. + * @len: number of bytes of *value: value[0]..value[len-1]. Initialy + * holds the sizeof value. + * + * Returns the value of one element inside a structure. + * + * If an element is OPTIONAL and the function "read_value" returns + * %ASN1_ELEMENT_NOT_FOUND, it means that this element wasn't present + * in the der encoding that created the structure. The first element + * of a SEQUENCE_OF or SET_OF is named "?1". The second one "?2" and + * so on. + * + * INTEGER: VALUE will contain a two's complement form integer. + * + * integer=-1 -> value[0]=0xFF , len=1. + * integer=1 -> value[0]=0x01 , len=1. + * + * ENUMERATED: As INTEGER (but only with not negative numbers). + * + * BOOLEAN: VALUE will be the null terminated string "TRUE" or + * "FALSE" and LEN=5 or LEN=6. + * + * OBJECT IDENTIFIER: VALUE will be a null terminated string with + * each number separated by a dot (i.e. "1.2.3.543.1"). + * + * LEN = strlen(VALUE)+1 + * + * UTCTime: VALUE will be a null terminated string in one of these + * formats: "YYMMDDhhmmss+hh'mm'" or "YYMMDDhhmmss-hh'mm'". + * LEN=strlen(VALUE)+1. + * + * GeneralizedTime: VALUE will be a null terminated string in the + * same format used to set the value. + * + * OCTET STRING: VALUE will contain the octet string and LEN will be + * the number of octets. + * + * GeneralString: VALUE will contain the generalstring and LEN will + * be the number of octets. + * + * BIT STRING: VALUE will contain the bit string organized by bytes + * and LEN will be the number of bits. + * + * CHOICE: If NAME indicates a choice type, VALUE will specify the + * alternative selected. + * + * ANY: If NAME indicates an any type, VALUE will indicate the DER + * encoding of the structure actually used. + * + * Returns: + * + * ASN1_SUCCESS: Set value OK. + * + * ASN1_ELEMENT_NOT_FOUND: NAME is not a valid element. + * + * ASN1_VALUE_NOT_FOUND: There isn't any value for the element selected. + * + * ASN1_MEM_ERROR: The value vector isn't big enough to store the result. + * In this case LEN will contain the number of bytes needed. + * + **/ +asn1_retCode +asn1_read_value (ASN1_TYPE root, const char *name, void *ivalue, int *len) +{ + node_asn *node, *p, *p2; + int len2, len3; + int value_size = *len; + unsigned char *value = ivalue; + + node = asn1_find_node (root, name); + if (node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + if ((type_field (node->type) != TYPE_NULL) && + (type_field (node->type) != TYPE_CHOICE) && + !(node->type & CONST_DEFAULT) && !(node->type & CONST_ASSIGN) && + (node->value == NULL)) + return ASN1_VALUE_NOT_FOUND; + + switch (type_field (node->type)) + { + case TYPE_NULL: + PUT_STR_VALUE (value, value_size, "NULL"); + break; + case TYPE_BOOLEAN: + if ((node->type & CONST_DEFAULT) && (node->value == NULL)) + { + p = node->down; + while (type_field (p->type) != TYPE_DEFAULT) + p = p->right; + if (p->type & CONST_TRUE) + { + PUT_STR_VALUE (value, value_size, "TRUE"); + } + else + { + PUT_STR_VALUE (value, value_size, "FALSE"); + } + } + else if (node->value[0] == 'T') + { + PUT_STR_VALUE (value, value_size, "TRUE"); + } + else + { + PUT_STR_VALUE (value, value_size, "FALSE"); + } + break; + case TYPE_INTEGER: + case TYPE_ENUMERATED: + if ((node->type & CONST_DEFAULT) && (node->value == NULL)) + { + p = node->down; + while (type_field (p->type) != TYPE_DEFAULT) + p = p->right; + if ((isdigit (p->value[0])) || (p->value[0] == '-') + || (p->value[0] == '+')) + { + if (_asn1_convert_integer + (p->value, value, value_size, len) != ASN1_SUCCESS) + return ASN1_MEM_ERROR; + } + else + { /* is an identifier like v1 */ + p2 = node->down; + while (p2) + { + if (type_field (p2->type) == TYPE_CONSTANT) + { + if ((p2->name) && (!strcmp (p2->name, p->value))) + { + if (_asn1_convert_integer + (p2->value, value, value_size, + len) != ASN1_SUCCESS) + return ASN1_MEM_ERROR; + break; + } + } + p2 = p2->right; + } + } + } + else + { + len2 = -1; + if (asn1_get_octet_der + (node->value, node->value_len, &len2, value, value_size, + len) != ASN1_SUCCESS) + return ASN1_MEM_ERROR; + } + break; + case TYPE_OBJECT_ID: + if (node->type & CONST_ASSIGN) + { + value[0] = 0; + p = node->down; + while (p) + { + if (type_field (p->type) == TYPE_CONSTANT) + { + ADD_STR_VALUE (value, value_size, p->value); + if (p->right) + { + ADD_STR_VALUE (value, value_size, "."); + } + } + p = p->right; + } + *len = strlen (value) + 1; + } + else if ((node->type & CONST_DEFAULT) && (node->value == NULL)) + { + p = node->down; + while (type_field (p->type) != TYPE_DEFAULT) + p = p->right; + PUT_STR_VALUE (value, value_size, p->value); + } + else + { + PUT_STR_VALUE (value, value_size, node->value); + } + break; + case TYPE_TIME: + PUT_STR_VALUE (value, value_size, node->value); + break; + case TYPE_OCTET_STRING: + len2 = -1; + if (asn1_get_octet_der + (node->value, node->value_len, &len2, value, value_size, + len) != ASN1_SUCCESS) + return ASN1_MEM_ERROR; + break; + case TYPE_GENERALSTRING: + len2 = -1; + if (asn1_get_octet_der + (node->value, node->value_len, &len2, value, value_size, + len) != ASN1_SUCCESS) + return ASN1_MEM_ERROR; + break; + case TYPE_BIT_STRING: + len2 = -1; + if (asn1_get_bit_der + (node->value, node->value_len, &len2, value, value_size, + len) != ASN1_SUCCESS) + return ASN1_MEM_ERROR; + break; + case TYPE_CHOICE: + PUT_STR_VALUE (value, value_size, node->down->name); + break; + case TYPE_ANY: + len3 = -1; + len2 = asn1_get_length_der (node->value, node->value_len, &len3); + if (len2 < 0) + return ASN1_DER_ERROR; + PUT_VALUE (value, value_size, node->value + len3, len2); + break; + default: + return ASN1_ELEMENT_NOT_FOUND; + break; + } + return ASN1_SUCCESS; +} + + +/** + * asn1_read_tag - Returns the TAG of one element inside a structure + * @root: pointer to a structure + * @name: the name of the element inside a structure. + * @tagValue: variable that will contain the TAG value. + * @classValue: variable that will specify the TAG type. + * + * Returns the TAG and the CLASS of one element inside a structure. + * CLASS can have one of these constants: %ASN1_CLASS_APPLICATION, + * %ASN1_CLASS_UNIVERSAL, %ASN1_CLASS_PRIVATE or + * %ASN1_CLASS_CONTEXT_SPECIFIC. + * + * Returns: + * + * ASN1_SUCCESS: Set value OK. + * + * ASN1_ELEMENT_NOT_FOUND: NAME is not a valid element. + * + **/ +asn1_retCode +asn1_read_tag (node_asn * root, const char *name, int *tagValue, + int *classValue) +{ + node_asn *node, *p, *pTag; + + node = asn1_find_node (root, name); + if (node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + p = node->down; + + /* pTag will points to the IMPLICIT TAG */ + pTag = NULL; + if (node->type & CONST_TAG) + { + while (p) + { + if (type_field (p->type) == TYPE_TAG) + { + if ((p->type & CONST_IMPLICIT) && (pTag == NULL)) + pTag = p; + else if (p->type & CONST_EXPLICIT) + pTag = NULL; + } + p = p->right; + } + } + + if (pTag) + { + *tagValue = strtoul (pTag->value, NULL, 10); + + if (pTag->type & CONST_APPLICATION) + *classValue = ASN1_CLASS_APPLICATION; + else if (pTag->type & CONST_UNIVERSAL) + *classValue = ASN1_CLASS_UNIVERSAL; + else if (pTag->type & CONST_PRIVATE) + *classValue = ASN1_CLASS_PRIVATE; + else + *classValue = ASN1_CLASS_CONTEXT_SPECIFIC; + } + else + { + *classValue = ASN1_CLASS_UNIVERSAL; + + switch (type_field (node->type)) + { + case TYPE_NULL: + *tagValue = ASN1_TAG_NULL; + break; + case TYPE_BOOLEAN: + *tagValue = ASN1_TAG_BOOLEAN; + break; + case TYPE_INTEGER: + *tagValue = ASN1_TAG_INTEGER; + break; + case TYPE_ENUMERATED: + *tagValue = ASN1_TAG_ENUMERATED; + break; + case TYPE_OBJECT_ID: + *tagValue = ASN1_TAG_OBJECT_ID; + break; + case TYPE_TIME: + if (node->type & CONST_UTC) + { + *tagValue = ASN1_TAG_UTCTime; + } + else + *tagValue = ASN1_TAG_GENERALIZEDTime; + break; + case TYPE_OCTET_STRING: + *tagValue = ASN1_TAG_OCTET_STRING; + break; + case TYPE_GENERALSTRING: + *tagValue = ASN1_TAG_GENERALSTRING; + break; + case TYPE_BIT_STRING: + *tagValue = ASN1_TAG_BIT_STRING; + break; + case TYPE_SEQUENCE: + case TYPE_SEQUENCE_OF: + *tagValue = ASN1_TAG_SEQUENCE; + break; + case TYPE_SET: + case TYPE_SET_OF: + *tagValue = ASN1_TAG_SET; + break; + case TYPE_TAG: + case TYPE_CHOICE: + case TYPE_ANY: + break; + default: + break; + } + } + + + return ASN1_SUCCESS; + +} diff --git a/src/daemon/https/minitasn1/element.h b/src/daemon/https/minitasn1/element.h new file mode 100644 index 00000000..3db95295 --- /dev/null +++ b/src/daemon/https/minitasn1/element.h @@ -0,0 +1,13 @@ + +#ifndef _ELEMENT_H +#define _ELEMENT_H + + +asn1_retCode _asn1_append_sequence_set(node_asn *node); + +asn1_retCode _asn1_convert_integer(const char *value,unsigned char *value_out, + int value_out_size, int *len); + +void _asn1_hierarchical_name(node_asn *node,char *name,int name_size); + +#endif diff --git a/src/daemon/https/minitasn1/errors.c b/src/daemon/https/minitasn1/errors.c new file mode 100644 index 00000000..544b3f01 --- /dev/null +++ b/src/daemon/https/minitasn1/errors.c @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2006 Free Software Foundation, Inc. + * Copyright (C) 2002, 2005 Fabio Fiorina + * + * This file is part of LIBTASN1. + * + * The LIBTASN1 library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include +#include "errors.h" +#ifdef STDC_HEADERS +# include +#endif + + +#define LIBTASN1_ERROR_ENTRY(name) \ + { #name, name } + +struct libtasn1_error_entry +{ + const char *name; + int number; +}; +typedef struct libtasn1_error_entry libtasn1_error_entry; + +static const libtasn1_error_entry error_algorithms[] = { + LIBTASN1_ERROR_ENTRY (ASN1_SUCCESS), + LIBTASN1_ERROR_ENTRY (ASN1_FILE_NOT_FOUND), + LIBTASN1_ERROR_ENTRY (ASN1_ELEMENT_NOT_FOUND), + LIBTASN1_ERROR_ENTRY (ASN1_IDENTIFIER_NOT_FOUND), + LIBTASN1_ERROR_ENTRY (ASN1_DER_ERROR), + LIBTASN1_ERROR_ENTRY (ASN1_VALUE_NOT_FOUND), + LIBTASN1_ERROR_ENTRY (ASN1_GENERIC_ERROR), + LIBTASN1_ERROR_ENTRY (ASN1_VALUE_NOT_VALID), + LIBTASN1_ERROR_ENTRY (ASN1_TAG_ERROR), + LIBTASN1_ERROR_ENTRY (ASN1_TAG_IMPLICIT), + LIBTASN1_ERROR_ENTRY (ASN1_ERROR_TYPE_ANY), + LIBTASN1_ERROR_ENTRY (ASN1_SYNTAX_ERROR), + LIBTASN1_ERROR_ENTRY (ASN1_MEM_ERROR), + LIBTASN1_ERROR_ENTRY (ASN1_MEM_ALLOC_ERROR), + LIBTASN1_ERROR_ENTRY (ASN1_DER_OVERFLOW), + LIBTASN1_ERROR_ENTRY (ASN1_NAME_TOO_LONG), + LIBTASN1_ERROR_ENTRY (ASN1_ARRAY_ERROR), + LIBTASN1_ERROR_ENTRY (ASN1_ELEMENT_NOT_EMPTY), + {0} +}; + +#define LIBTASN1_ERROR_LOOP(b) \ + const libtasn1_error_entry *p; \ + for(p = error_algorithms; p->name != NULL; p++) { b ; } + +#define LIBTASN1_ERROR_ALG_LOOP(a) \ + LIBTASN1_ERROR_LOOP( if(p->number == error) { a; break; } ) + + + +/** + * libtasn1_perror - prints a string to stderr with a description of an error + * @error: is an error returned by a libtasn1 function. + * + * This function is like perror(). The only difference is that it + * accepts an error returned by a libtasn1 function. + **/ +void +libtasn1_perror (asn1_retCode error) +{ + const char *ret = NULL; + + /* avoid prefix */ + LIBTASN1_ERROR_ALG_LOOP (ret = p->name + sizeof ("ASN1_") - 1); + + fprintf (stderr, "LIBTASN1 ERROR: %s\n", ret); + +} + + +/** + * libtasn1_strerror - Returns a string with a description of an error + * @error: is an error returned by a libtasn1 function. + * + * This function is similar to strerror(). The only difference is + * that it accepts an error (number) returned by a libtasn1 function. + * + * Returns: Pointer to static zero-terminated string describing error + * code. + **/ +const char * +libtasn1_strerror (asn1_retCode error) +{ + const char *ret = NULL; + + /* avoid prefix */ + LIBTASN1_ERROR_ALG_LOOP (ret = p->name + sizeof ("ASN1_") - 1); + + return ret; +} + +/* this function will output a message. + */ +#ifdef LIBTASN1_DEBUG +void +_libtasn1_log (const char *fmt, ...) +{ + va_list args; + char str[MAX_LOG_SIZE]; + + va_start (args, fmt); + vsprintf (str, fmt, args); /* Flawfinder: ignore */ + va_end (args); + + fprintf (stderr, str); + + return; +} +#else /* not DEBUG */ +void +_libtasn1_log (const char *fmt, ...) +{ + return; +} +#endif /* DEBUG */ diff --git a/src/daemon/https/minitasn1/errors.h b/src/daemon/https/minitasn1/errors.h new file mode 100644 index 00000000..f8bf2242 --- /dev/null +++ b/src/daemon/https/minitasn1/errors.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2004, 2006 Free Software Foundation, Inc. + * Copyright (C) 2002 Fabio Fiorina + * + * This file is part of LIBTASN1. + * + * The LIBTASN1 library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef ERRORS_H +#define ERRORS_H + +#include "int.h" + +void _libtasn1_log( const char *fmt, ...); + +#endif /* ERRORS_H */ diff --git a/src/daemon/https/minitasn1/gstr.c b/src/daemon/https/minitasn1/gstr.c new file mode 100644 index 00000000..0430ee1a --- /dev/null +++ b/src/daemon/https/minitasn1/gstr.c @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2006 Free Software Foundation + * Copyright (C) 2002 Nikos Mavroyanopoulos + * + * This file is part of LIBTASN1. + * + * The LIBTASN1 library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include + +/* These function are like strcat, strcpy. They only + * do bounds checking (they shouldn't cause buffer overruns), + * and they always produce null terminated strings. + * + * They should be used only with null terminated strings. + */ +void +_asn1_str_cat (char *dest, size_t dest_tot_size, const char *src) +{ + size_t str_size = strlen (src); + size_t dest_size = strlen (dest); + + if (dest_tot_size - dest_size > str_size) + { + strcat (dest, src); + } + else + { + if (dest_tot_size - dest_size > 0) + { + strncat (dest, src, (dest_tot_size - dest_size) - 1); + dest[dest_tot_size - 1] = 0; + } + } +} + +void +_asn1_str_cpy (char *dest, size_t dest_tot_size, const char *src) +{ + size_t str_size = strlen (src); + + if (dest_tot_size > str_size) + { + strcpy (dest, src); + } + else + { + if (dest_tot_size > 0) + { + strncpy (dest, src, (dest_tot_size) - 1); + dest[dest_tot_size - 1] = 0; + } + } +} diff --git a/src/daemon/https/minitasn1/gstr.h b/src/daemon/https/minitasn1/gstr.h new file mode 100644 index 00000000..5508d26e --- /dev/null +++ b/src/daemon/https/minitasn1/gstr.h @@ -0,0 +1,5 @@ +void _asn1_str_cpy( char* dest, size_t dest_tot_size, const char* src); +void _asn1_str_cat( char* dest, size_t dest_tot_size, const char* src); + +#define Estrcpy(x,y) _asn1_str_cpy(x,MAX_ERROR_DESCRIPTION_SIZE,y) +#define Estrcat(x,y) _asn1_str_cat(x,MAX_ERROR_DESCRIPTION_SIZE,y) diff --git a/src/daemon/https/minitasn1/int.h b/src/daemon/https/minitasn1/int.h new file mode 100644 index 00000000..d9d18c77 --- /dev/null +++ b/src/daemon/https/minitasn1/int.h @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2004, 2006 Free Software Foundation, Inc. + * Copyright (C) 2002 Fabio Fiorina + * + * This file is part of LIBTASN1. + * + * The LIBTASN1 library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef INT_H +#define INT_H + +#include +#include + +/* +#define LIBTASN1_DEBUG +#define LIBTASN1_DEBUG_PARSER +#define LIBTASN1_DEBUG_INTEGER +*/ + +#include + +#define MAX_LOG_SIZE 1024 /* maximum number of characters of a log message */ + +/* Define used for visiting trees. */ +#define UP 1 +#define RIGHT 2 +#define DOWN 3 + +/****************************************/ +/* Returns the first 8 bits. */ +/* Used with the field type of node_asn */ +/****************************************/ +#define type_field(x) (x&0xFF) + +/* List of constants for field type of typedef node_asn */ +#define TYPE_CONSTANT 1 +#define TYPE_IDENTIFIER 2 +#define TYPE_INTEGER 3 +#define TYPE_BOOLEAN 4 +#define TYPE_SEQUENCE 5 +#define TYPE_BIT_STRING 6 +#define TYPE_OCTET_STRING 7 +#define TYPE_TAG 8 +#define TYPE_DEFAULT 9 +#define TYPE_SIZE 10 +#define TYPE_SEQUENCE_OF 11 +#define TYPE_OBJECT_ID 12 +#define TYPE_ANY 13 +#define TYPE_SET 14 +#define TYPE_SET_OF 15 +#define TYPE_DEFINITIONS 16 +#define TYPE_TIME 17 +#define TYPE_CHOICE 18 +#define TYPE_IMPORTS 19 +#define TYPE_NULL 20 +#define TYPE_ENUMERATED 21 +#define TYPE_GENERALSTRING 27 + + +/***********************************************************************/ +/* List of constants to better specify the type of typedef node_asn. */ +/***********************************************************************/ +/* Used with TYPE_TAG */ +#define CONST_UNIVERSAL (1<<8) +#define CONST_PRIVATE (1<<9) +#define CONST_APPLICATION (1<<10) +#define CONST_EXPLICIT (1<<11) +#define CONST_IMPLICIT (1<<12) + +#define CONST_TAG (1<<13) /* Used in ASN.1 assignement */ +#define CONST_OPTION (1<<14) +#define CONST_DEFAULT (1<<15) +#define CONST_TRUE (1<<16) +#define CONST_FALSE (1<<17) + +#define CONST_LIST (1<<18) /* Used with TYPE_INTEGER and TYPE_BIT_STRING */ +#define CONST_MIN_MAX (1<<19) + +#define CONST_1_PARAM (1<<20) + +#define CONST_SIZE (1<<21) + +#define CONST_DEFINED_BY (1<<22) + +#define CONST_GENERALIZED (1<<23) +#define CONST_UTC (1<<24) + +/* #define CONST_IMPORTS (1<<25) */ + +#define CONST_NOT_USED (1<<26) +#define CONST_SET (1<<27) +#define CONST_ASSIGN (1<<28) + +#define CONST_DOWN (1<<29) +#define CONST_RIGHT (1<<30) + +#endif /* INT_H */ diff --git a/src/daemon/https/minitasn1/libtasn1.h b/src/daemon/https/minitasn1/libtasn1.h new file mode 100644 index 00000000..0b48c305 --- /dev/null +++ b/src/daemon/https/minitasn1/libtasn1.h @@ -0,0 +1,246 @@ +/* + * Copyright (C) 2004, 2005, 2006 Free Software Foundation + * Copyright (C) 2002 Fabio Fiorina + * + * This file is part of LIBTASN1. + * + * LIBTASN1 is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * LIBTASN1 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with LIBTASN1; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + +#ifndef LIBTASN1_H +# define LIBTASN1_H + +#include /* for FILE* */ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define LIBTASN1_VERSION "1.2" + +#include +#include + +#define MAX_NAME_SIZE 128 /* maximum number of characters of a name */ + /* inside a file with ASN1 definitons */ +#define MAX_ERROR_DESCRIPTION_SIZE 128 /* maximum number of characters */ + /* of a description message */ + /* (null character included) */ + + + typedef int asn1_retCode; /* type returned by libtasn1 functions */ + + /*****************************************/ + /* Errors returned by libtasn1 functions */ + /*****************************************/ +#define ASN1_SUCCESS 0 +#define ASN1_FILE_NOT_FOUND 1 +#define ASN1_ELEMENT_NOT_FOUND 2 +#define ASN1_IDENTIFIER_NOT_FOUND 3 +#define ASN1_DER_ERROR 4 +#define ASN1_VALUE_NOT_FOUND 5 +#define ASN1_GENERIC_ERROR 6 +#define ASN1_VALUE_NOT_VALID 7 +#define ASN1_TAG_ERROR 8 +#define ASN1_TAG_IMPLICIT 9 +#define ASN1_ERROR_TYPE_ANY 10 +#define ASN1_SYNTAX_ERROR 11 +#define ASN1_MEM_ERROR 12 +#define ASN1_MEM_ALLOC_ERROR 13 +#define ASN1_DER_OVERFLOW 14 +#define ASN1_NAME_TOO_LONG 15 +#define ASN1_ARRAY_ERROR 16 +#define ASN1_ELEMENT_NOT_EMPTY 17 + +/*************************************/ +/* Constants used in asn1_visit_tree */ +/*************************************/ +#define ASN1_PRINT_NAME 1 +#define ASN1_PRINT_NAME_TYPE 2 +#define ASN1_PRINT_NAME_TYPE_VALUE 3 +#define ASN1_PRINT_ALL 4 + +/*****************************************/ +/* Constants returned by asn1_read_tag */ +/*****************************************/ +#define ASN1_CLASS_UNIVERSAL 0x00 /* old: 1 */ +#define ASN1_CLASS_APPLICATION 0x40 /* old: 2 */ +#define ASN1_CLASS_CONTEXT_SPECIFIC 0x80 /* old: 3 */ +#define ASN1_CLASS_PRIVATE 0xC0 /* old: 4 */ +#define ASN1_CLASS_STRUCTURED 0x20 + +/*****************************************/ +/* Constants returned by asn1_read_tag */ +/*****************************************/ +#define ASN1_TAG_BOOLEAN 0x01 +#define ASN1_TAG_INTEGER 0x02 +#define ASN1_TAG_SEQUENCE 0x10 +#define ASN1_TAG_SET 0x11 +#define ASN1_TAG_OCTET_STRING 0x04 +#define ASN1_TAG_BIT_STRING 0x03 +#define ASN1_TAG_UTCTime 0x17 +#define ASN1_TAG_GENERALIZEDTime 0x18 +#define ASN1_TAG_OBJECT_ID 0x06 +#define ASN1_TAG_ENUMERATED 0x0A +#define ASN1_TAG_NULL 0x05 +#define ASN1_TAG_GENERALSTRING 0x1B + +/******************************************************/ +/* Structure definition used for the node of the tree */ +/* that represent an ASN.1 DEFINITION. */ +/******************************************************/ + + struct node_asn_struct + { + char *name; /* Node name */ + unsigned int type; /* Node type */ + unsigned char *value; /* Node value */ + int value_len; + struct node_asn_struct *down; /* Pointer to the son node */ + struct node_asn_struct *right; /* Pointer to the brother node */ + struct node_asn_struct *left; /* Pointer to the next list element */ + }; + + typedef struct node_asn_struct node_asn; + + typedef node_asn *ASN1_TYPE; + +#define ASN1_TYPE_EMPTY NULL + + struct static_struct_asn + { + const char *name; /* Node name */ + unsigned int type; /* Node type */ + const void *value; /* Node value */ + }; + + typedef struct static_struct_asn ASN1_ARRAY_TYPE; + + + + /***********************************/ + /* Functions definitions */ + /***********************************/ + + asn1_retCode asn1_parser2tree (const char *file_name, + ASN1_TYPE * definitions, + char *errorDescription); + + asn1_retCode asn1_parser2array (const char *inputFileName, + const char *outputFileName, + const char *vectorName, + char *errorDescription); + + asn1_retCode asn1_array2tree (const ASN1_ARRAY_TYPE * array, + ASN1_TYPE * definitions, + char *errorDescription); + + void asn1_print_structure (FILE *out, ASN1_TYPE structure, const char *name, + int mode); + + asn1_retCode asn1_create_element (ASN1_TYPE definitions, + const char *source_name, + ASN1_TYPE * element); + + asn1_retCode asn1_delete_structure (ASN1_TYPE * structure); + + asn1_retCode asn1_delete_element (ASN1_TYPE structure, + const char *element_name); + + asn1_retCode asn1_write_value (ASN1_TYPE node_root, const char *name, + const void *ivalue, int len); + + asn1_retCode asn1_read_value (ASN1_TYPE root, const char *name, + void *ivalue, int *len); + + asn1_retCode asn1_number_of_elements (ASN1_TYPE element, const char *name, + int *num); + + asn1_retCode asn1_der_coding (ASN1_TYPE element, const char *name, + void *ider, int *len, char *ErrorDescription); + + asn1_retCode asn1_der_decoding (ASN1_TYPE * element, const void *ider, + int len, char *errorDescription); + + asn1_retCode asn1_der_decoding_element (ASN1_TYPE * structure, + const char *elementName, + const void *ider, int len, + char *errorDescription); + + asn1_retCode asn1_der_decoding_startEnd (ASN1_TYPE element, + const void *ider, int len, + const char *name_element, + int *start, int *end); + + asn1_retCode asn1_expand_any_defined_by (ASN1_TYPE definitions, + ASN1_TYPE * element); + + asn1_retCode asn1_expand_octet_string (ASN1_TYPE definitions, + ASN1_TYPE * element, + const char *octetName, + const char *objectName); + + asn1_retCode asn1_read_tag (node_asn * root, const char *name, + int *tagValue, int *classValue); + + const char *asn1_find_structure_from_oid (ASN1_TYPE definitions, + const char *oidValue); + + const char *asn1_check_version (const char *req_version); + + const char *libtasn1_strerror (asn1_retCode error); + + void libtasn1_perror (asn1_retCode error); + + /* DER utility functions. */ + + int asn1_get_tag_der (const unsigned char *der, int der_len, + unsigned char *cls, int *len, unsigned long *tag); + + void asn1_octet_der (const unsigned char *str, int str_len, + unsigned char *der, int *der_len); + + asn1_retCode asn1_get_octet_der (const unsigned char *der, int der_len, + int *ret_len, unsigned char *str, + int str_size, int *str_len); + + void asn1_bit_der (const unsigned char *str, int bit_len, + unsigned char *der, int *der_len); + + asn1_retCode asn1_get_bit_der (const unsigned char *der, int der_len, + int *ret_len, unsigned char *str, + int str_size, int *bit_len); + + signed long asn1_get_length_der (const unsigned char *der, int der_len, + int *len); + + void asn1_length_der (unsigned long int len, unsigned char *ans, + int *ans_len); + + /* Other utility functions. */ + + ASN1_TYPE asn1_find_node (ASN1_TYPE pointer, const char *name); + + asn1_retCode asn1_copy_node (ASN1_TYPE dst, const char *dst_name, + ASN1_TYPE src, const char *src_name); + +#ifdef __cplusplus +} +#endif + +#endif /* LIBTASN1_H */ diff --git a/src/daemon/https/minitasn1/mem.h b/src/daemon/https/minitasn1/mem.h new file mode 100644 index 00000000..267f62f3 --- /dev/null +++ b/src/daemon/https/minitasn1/mem.h @@ -0,0 +1,27 @@ +#ifndef MEM_H +# define MEM_H + +/* Use _asn1_afree() when calling alloca, or + * memory leaks may occur in systems which do not + * support alloca. + */ +#ifdef HAVE_ALLOCA +# ifdef HAVE_ALLOCA_H +# include +# endif +# define _asn1_alloca alloca +# define _asn1_afree(x) +#else +# define _asn1_alloca _asn1_malloc +# define _asn1_afree _asn1_free +#endif /* HAVE_ALLOCA */ + +#define _asn1_malloc malloc +#define _asn1_free free +#define _asn1_calloc calloc +#define _asn1_realloc realloc +#define _asn1_strdup strdup + +#endif /* MEM_H */ + + diff --git a/src/daemon/https/minitasn1/parser_aux.c b/src/daemon/https/minitasn1/parser_aux.c new file mode 100644 index 00000000..7d975b3d --- /dev/null +++ b/src/daemon/https/minitasn1/parser_aux.c @@ -0,0 +1,1072 @@ +/* + * Copyright (C) 2004, 2006, 2007 Free Software Foundation + * Copyright (C) 2000,2001 Fabio Fiorina + * + * This file is part of LIBTASN1. + * + * The LIBTASN1 library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include +#include +#include "parser_aux.h" +#include "gstr.h" +#include "structure.h" +#include "element.h" + +char _asn1_identifierMissing[MAX_NAME_SIZE + 1]; /* identifier name not found */ + +/***********************************************/ +/* Type: list_type */ +/* Description: type used in the list during */ +/* the structure creation. */ +/***********************************************/ +typedef struct list_struct +{ + node_asn *node; + struct list_struct *next; +} list_type; + + +/* Pointer to the first element of the list */ +list_type *firstElement = NULL; + +/******************************************************/ +/* Function : _asn1_add_node */ +/* Description: creates a new NODE_ASN element and */ +/* puts it in the list pointed by firstElement. */ +/* Parameters: */ +/* type: type of the new element (see TYPE_ */ +/* and CONST_ constants). */ +/* Return: pointer to the new element. */ +/******************************************************/ +node_asn * +_asn1_add_node (unsigned int type) +{ + list_type *listElement; + node_asn *punt; + + punt = (node_asn *) _asn1_calloc (1, sizeof (node_asn)); + if (punt == NULL) + return NULL; + + listElement = (list_type *) _asn1_malloc (sizeof (list_type)); + if (listElement == NULL) + { + _asn1_free (punt); + return NULL; + } + + listElement->node = punt; + listElement->next = firstElement; + firstElement = listElement; + + punt->type = type; + + return punt; +} + +/** + * asn1_find_node: + * @pointer: NODE_ASN element pointer. + * @name: null terminated string with the element's name to find. + * + * Searches for an element called NAME starting from POINTER. The + * name is composed by differents identifiers separated by dots. When + * *POINTER has a name, the first identifier must be the name of + * *POINTER, otherwise it must be the name of one child of *POINTER. + * + * Return value: the searching result. NULL if not found. + **/ +ASN1_TYPE +asn1_find_node (ASN1_TYPE pointer, const char *name) +{ + node_asn *p; + char *n_end, n[MAX_NAME_SIZE + 1]; + const char *n_start; + + if (pointer == NULL) + return NULL; + + if (name == NULL) + return NULL; + + p = pointer; + n_start = name; + + if (p->name != NULL) + { /* has *pointer got a name ? */ + n_end = strchr (n_start, '.'); /* search the first dot */ + if (n_end) + { + memcpy (n, n_start, n_end - n_start); + n[n_end - n_start] = 0; + n_start = n_end; + n_start++; + } + else + { + _asn1_str_cpy (n, sizeof (n), n_start); + n_start = NULL; + } + + while (p) + { + if ((p->name) && (!strcmp (p->name, n))) + break; + else + p = p->right; + } /* while */ + + if (p == NULL) + return NULL; + } + else + { /* *pointer doesn't have a name */ + if (n_start[0] == 0) + return p; + } + + while (n_start) + { /* Has the end of NAME been reached? */ + n_end = strchr (n_start, '.'); /* search the next dot */ + if (n_end) + { + memcpy (n, n_start, n_end - n_start); + n[n_end - n_start] = 0; + n_start = n_end; + n_start++; + } + else + { + _asn1_str_cpy (n, sizeof (n), n_start); + n_start = NULL; + } + + if (p->down == NULL) + return NULL; + + p = p->down; + + /* The identifier "?LAST" indicates the last element + in the right chain. */ + if (!strcmp (n, "?LAST")) + { + if (p == NULL) + return NULL; + while (p->right) + p = p->right; + } + else + { /* no "?LAST" */ + while (p) + { + if ((p->name) && (!strcmp (p->name, n))) + break; + else + p = p->right; + } + if (p == NULL) + return NULL; + } + } /* while */ + + return p; +} + + +/******************************************************************/ +/* Function : _asn1_set_value */ +/* Description: sets the field VALUE in a NODE_ASN element. The */ +/* previous value (if exist) will be lost */ +/* Parameters: */ +/* node: element pointer. */ +/* value: pointer to the value that you want to set. */ +/* len: character number of value. */ +/* Return: pointer to the NODE_ASN element. */ +/******************************************************************/ +node_asn * +_asn1_set_value (node_asn * node, const void *_value, unsigned int len) +{ + const unsigned char *value = _value; + + if (node == NULL) + return node; + if (node->value) + { + _asn1_free (node->value); + node->value = NULL; + node->value_len = 0; + } + if (!len) + return node; + node->value = (unsigned char *) _asn1_malloc (len); + if (node->value == NULL) + return NULL; + node->value_len = len; + + memcpy (node->value, value, len); + return node; +} + +/******************************************************************/ +/* Function : _asn1_set_name */ +/* Description: sets the field NAME in a NODE_ASN element. The */ +/* previous value (if exist) will be lost */ +/* Parameters: */ +/* node: element pointer. */ +/* name: a null terminated string with the name that you want */ +/* to set. */ +/* Return: pointer to the NODE_ASN element. */ +/******************************************************************/ +node_asn * +_asn1_set_name (node_asn * node, const char *name) +{ + if (node == NULL) + return node; + + if (node->name) + { + _asn1_free (node->name); + node->name = NULL; + } + + if (name == NULL) + return node; + + if (strlen (name)) + { + node->name = (char *) _asn1_strdup (name); + if (node->name == NULL) + return NULL; + } + else + node->name = NULL; + return node; +} + +/******************************************************************/ +/* Function : _asn1_set_right */ +/* Description: sets the field RIGHT in a NODE_ASN element. */ +/* Parameters: */ +/* node: element pointer. */ +/* right: pointer to a NODE_ASN element that you want be pointed*/ +/* by NODE. */ +/* Return: pointer to *NODE. */ +/******************************************************************/ +node_asn * +_asn1_set_right (node_asn * node, node_asn * right) +{ + if (node == NULL) + return node; + node->right = right; + if (right) + right->left = node; + return node; +} + +/******************************************************************/ +/* Function : _asn1_get_right */ +/* Description: returns the element pointed by the RIGHT field of */ +/* a NODE_ASN element. */ +/* Parameters: */ +/* node: NODE_ASN element pointer. */ +/* Return: field RIGHT of NODE. */ +/******************************************************************/ +node_asn * +_asn1_get_right (node_asn * node) +{ + if (node == NULL) + return NULL; + return node->right; +} + +/******************************************************************/ +/* Function : _asn1_get_last_right */ +/* Description: return the last element along the right chain. */ +/* Parameters: */ +/* node: starting element pointer. */ +/* Return: pointer to the last element along the right chain. */ +/******************************************************************/ +node_asn * +_asn1_get_last_right (node_asn * node) +{ + node_asn *p; + + if (node == NULL) + return NULL; + p = node; + while (p->right) + p = p->right; + return p; +} + +/******************************************************************/ +/* Function : _asn1_set_down */ +/* Description: sets the field DOWN in a NODE_ASN element. */ +/* Parameters: */ +/* node: element pointer. */ +/* down: pointer to a NODE_ASN element that you want be pointed */ +/* by NODE. */ +/* Return: pointer to *NODE. */ +/******************************************************************/ +node_asn * +_asn1_set_down (node_asn * node, node_asn * down) +{ + if (node == NULL) + return node; + node->down = down; + if (down) + down->left = node; + return node; +} + +/******************************************************************/ +/* Function : _asn1_get_down */ +/* Description: returns the element pointed by the DOWN field of */ +/* a NODE_ASN element. */ +/* Parameters: */ +/* node: NODE_ASN element pointer. */ +/* Return: field DOWN of NODE. */ +/******************************************************************/ +node_asn * +_asn1_get_down (node_asn * node) +{ + if (node == NULL) + return NULL; + return node->down; +} + +/******************************************************************/ +/* Function : _asn1_get_name */ +/* Description: returns the name of a NODE_ASN element. */ +/* Parameters: */ +/* node: NODE_ASN element pointer. */ +/* Return: a null terminated string. */ +/******************************************************************/ +char * +_asn1_get_name (node_asn * node) +{ + if (node == NULL) + return NULL; + return node->name; +} + +/******************************************************************/ +/* Function : _asn1_mod_type */ +/* Description: change the field TYPE of an NODE_ASN element. */ +/* The new value is the old one | (bitwise or) the */ +/* paramener VALUE. */ +/* Parameters: */ +/* node: NODE_ASN element pointer. */ +/* value: the integer value that must be or-ed with the current */ +/* value of field TYPE. */ +/* Return: NODE pointer. */ +/******************************************************************/ +node_asn * +_asn1_mod_type (node_asn * node, unsigned int value) +{ + if (node == NULL) + return node; + node->type |= value; + return node; +} + + +/******************************************************************/ +/* Function : _asn1_remove_node */ +/* Description: gets free the memory allocated for an NODE_ASN */ +/* element (not the elements pointed by it). */ +/* Parameters: */ +/* node: NODE_ASN element pointer. */ +/******************************************************************/ +void +_asn1_remove_node (node_asn * node) +{ + if (node == NULL) + return; + + if (node->name != NULL) + _asn1_free (node->name); + if (node->value != NULL) + _asn1_free (node->value); + _asn1_free (node); +} + +/******************************************************************/ +/* Function : _asn1_find_up */ +/* Description: return the father of the NODE_ASN element. */ +/* Parameters: */ +/* node: NODE_ASN element pointer. */ +/* Return: Null if not found. */ +/******************************************************************/ +node_asn * +_asn1_find_up (node_asn * node) +{ + node_asn *p; + + if (node == NULL) + return NULL; + + p = node; + + while ((p->left != NULL) && (p->left->right == p)) + p = p->left; + + return p->left; +} + +/******************************************************************/ +/* Function : _asn1_delete_list */ +/* Description: deletes the list elements (not the elements */ +/* pointed by them). */ +/******************************************************************/ +void +_asn1_delete_list (void) +{ + list_type *listElement; + + while (firstElement) + { + listElement = firstElement; + firstElement = firstElement->next; + _asn1_free (listElement); + } +} + +/******************************************************************/ +/* Function : _asn1_delete_list_and nodes */ +/* Description: deletes the list elements and the elements */ +/* pointed by them. */ +/******************************************************************/ +void +_asn1_delete_list_and_nodes (void) +{ + list_type *listElement; + + while (firstElement) + { + listElement = firstElement; + firstElement = firstElement->next; + _asn1_remove_node (listElement->node); + _asn1_free (listElement); + } +} + + +char * +_asn1_ltostr (long v, char *str) +{ + long d, r; + char temp[20]; + int count, k, start; + + if (v < 0) + { + str[0] = '-'; + start = 1; + v = -v; + } + else + start = 0; + + count = 0; + do + { + d = v / 10; + r = v - d * 10; + temp[start + count] = '0' + (char) r; + count++; + v = d; + } + while (v); + + for (k = 0; k < count; k++) + str[k + start] = temp[start + count - k - 1]; + str[count + start] = 0; + return str; +} + + +/******************************************************************/ +/* Function : _asn1_change_integer_value */ +/* Description: converts into DER coding the value assign to an */ +/* INTEGER constant. */ +/* Parameters: */ +/* node: root of an ASN1element. */ +/* Return: */ +/* ASN1_ELEMENT_NOT_FOUND if NODE is NULL, */ +/* otherwise ASN1_SUCCESS */ +/******************************************************************/ +asn1_retCode +_asn1_change_integer_value (ASN1_TYPE node) +{ + node_asn *p; + unsigned char val[SIZEOF_UNSIGNED_LONG_INT]; + unsigned char val2[SIZEOF_UNSIGNED_LONG_INT + 1]; + int len; + + if (node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + p = node; + while (p) + { + if ((type_field (p->type) == TYPE_INTEGER) && (p->type & CONST_ASSIGN)) + { + if (p->value) + { + _asn1_convert_integer (p->value, val, sizeof (val), &len); + asn1_octet_der (val, len, val2, &len); + _asn1_set_value (p, val2, len); + } + } + + if (p->down) + { + p = p->down; + } + else + { + if (p == node) + p = NULL; + else if (p->right) + p = p->right; + else + { + while (1) + { + p = _asn1_find_up (p); + if (p == node) + { + p = NULL; + break; + } + if (p->right) + { + p = p->right; + break; + } + } + } + } + } + + return ASN1_SUCCESS; +} + + +/******************************************************************/ +/* Function : _asn1_expand_object_id */ +/* Description: expand the IDs of an OBJECT IDENTIFIER constant. */ +/* Parameters: */ +/* node: root of an ASN1 element. */ +/* Return: */ +/* ASN1_ELEMENT_NOT_FOUND if NODE is NULL, */ +/* otherwise ASN1_SUCCESS */ +/******************************************************************/ +asn1_retCode +_asn1_expand_object_id (ASN1_TYPE node) +{ + node_asn *p, *p2, *p3, *p4, *p5; + char name_root[MAX_NAME_SIZE], name2[2 * MAX_NAME_SIZE + 1]; + int move, tlen; + + if (node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + _asn1_str_cpy (name_root, sizeof (name_root), node->name); + + p = node; + move = DOWN; + + while (!((p == node) && (move == UP))) + { + if (move != UP) + { + if ((type_field (p->type) == TYPE_OBJECT_ID) + && (p->type & CONST_ASSIGN)) + { + p2 = p->down; + if (p2 && (type_field (p2->type) == TYPE_CONSTANT)) + { + if (p2->value && !isdigit (p2->value[0])) + { + _asn1_str_cpy (name2, sizeof (name2), name_root); + _asn1_str_cat (name2, sizeof (name2), "."); + _asn1_str_cat (name2, sizeof (name2), p2->value); + p3 = asn1_find_node (node, name2); + if (!p3 || (type_field (p3->type) != TYPE_OBJECT_ID) || + !(p3->type & CONST_ASSIGN)) + return ASN1_ELEMENT_NOT_FOUND; + _asn1_set_down (p, p2->right); + _asn1_remove_node (p2); + p2 = p; + p4 = p3->down; + while (p4) + { + if (type_field (p4->type) == TYPE_CONSTANT) + { + p5 = _asn1_add_node_only (TYPE_CONSTANT); + _asn1_set_name (p5, p4->name); + tlen = strlen (p4->value); + if (tlen > 0) + _asn1_set_value (p5, p4->value, tlen + 1); + if (p2 == p) + { + _asn1_set_right (p5, p->down); + _asn1_set_down (p, p5); + } + else + { + _asn1_set_right (p5, p2->right); + _asn1_set_right (p2, p5); + } + p2 = p5; + } + p4 = p4->right; + } + move = DOWN; + continue; + } + } + } + move = DOWN; + } + else + move = RIGHT; + + if (move == DOWN) + { + if (p->down) + p = p->down; + else + move = RIGHT; + } + + if (p == node) + { + move = UP; + continue; + } + + if (move == RIGHT) + { + if (p->right) + p = p->right; + else + move = UP; + } + if (move == UP) + p = _asn1_find_up (p); + } + + + /*******************************/ + /* expand DEFAULT */ + /*******************************/ + p = node; + move = DOWN; + + while (!((p == node) && (move == UP))) + { + if (move != UP) + { + if ((type_field (p->type) == TYPE_OBJECT_ID) && + (p->type & CONST_DEFAULT)) + { + p2 = p->down; + if (p2 && (type_field (p2->type) == TYPE_DEFAULT)) + { + _asn1_str_cpy (name2, sizeof (name2), name_root); + _asn1_str_cat (name2, sizeof (name2), "."); + _asn1_str_cat (name2, sizeof (name2), p2->value); + p3 = asn1_find_node (node, name2); + if (!p3 || (type_field (p3->type) != TYPE_OBJECT_ID) || + !(p3->type & CONST_ASSIGN)) + return ASN1_ELEMENT_NOT_FOUND; + p4 = p3->down; + name2[0] = 0; + while (p4) + { + if (type_field (p4->type) == TYPE_CONSTANT) + { + if (name2[0]) + _asn1_str_cat (name2, sizeof (name2), "."); + _asn1_str_cat (name2, sizeof (name2), p4->value); + } + p4 = p4->right; + } + tlen = strlen (name2); + if (tlen > 0) + _asn1_set_value (p2, name2, tlen + 1); + } + } + move = DOWN; + } + else + move = RIGHT; + + if (move == DOWN) + { + if (p->down) + p = p->down; + else + move = RIGHT; + } + + if (p == node) + { + move = UP; + continue; + } + + if (move == RIGHT) + { + if (p->right) + p = p->right; + else + move = UP; + } + if (move == UP) + p = _asn1_find_up (p); + } + + return ASN1_SUCCESS; +} + + +/******************************************************************/ +/* Function : _asn1_type_set_config */ +/* Description: sets the CONST_SET and CONST_NOT_USED properties */ +/* in the fields of the SET elements. */ +/* Parameters: */ +/* node: root of an ASN1 element. */ +/* Return: */ +/* ASN1_ELEMENT_NOT_FOUND if NODE is NULL, */ +/* otherwise ASN1_SUCCESS */ +/******************************************************************/ +asn1_retCode +_asn1_type_set_config (ASN1_TYPE node) +{ + node_asn *p, *p2; + int move; + + if (node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + p = node; + move = DOWN; + + while (!((p == node) && (move == UP))) + { + if (move != UP) + { + if (type_field (p->type) == TYPE_SET) + { + p2 = p->down; + while (p2) + { + if (type_field (p2->type) != TYPE_TAG) + p2->type |= CONST_SET | CONST_NOT_USED; + p2 = p2->right; + } + } + move = DOWN; + } + else + move = RIGHT; + + if (move == DOWN) + { + if (p->down) + p = p->down; + else + move = RIGHT; + } + + if (p == node) + { + move = UP; + continue; + } + + if (move == RIGHT) + { + if (p->right) + p = p->right; + else + move = UP; + } + if (move == UP) + p = _asn1_find_up (p); + } + + return ASN1_SUCCESS; +} + + +/******************************************************************/ +/* Function : _asn1_check_identifier */ +/* Description: checks the definitions of all the identifiers */ +/* and the first element of an OBJECT_ID (e.g. {pkix 0 4}). */ +/* The _asn1_identifierMissing global variable is filled if */ +/* necessary. */ +/* Parameters: */ +/* node: root of an ASN1 element. */ +/* Return: */ +/* ASN1_ELEMENT_NOT_FOUND if NODE is NULL, */ +/* ASN1_IDENTIFIER_NOT_FOUND if an identifier is not defined, */ +/* otherwise ASN1_SUCCESS */ +/******************************************************************/ +asn1_retCode +_asn1_check_identifier (ASN1_TYPE node) +{ + node_asn *p, *p2; + char name2[MAX_NAME_SIZE * 2 + 2]; + + if (node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + p = node; + while (p) + { + if (type_field (p->type) == TYPE_IDENTIFIER) + { + _asn1_str_cpy (name2, sizeof (name2), node->name); + _asn1_str_cat (name2, sizeof (name2), "."); + _asn1_str_cat (name2, sizeof (name2), p->value); + p2 = asn1_find_node (node, name2); + if (p2 == NULL) + { + strcpy (_asn1_identifierMissing, p->value); + return ASN1_IDENTIFIER_NOT_FOUND; + } + } + else if ((type_field (p->type) == TYPE_OBJECT_ID) && + (p->type & CONST_DEFAULT)) + { + p2 = p->down; + if (p2 && (type_field (p2->type) == TYPE_DEFAULT)) + { + _asn1_str_cpy (name2, sizeof (name2), node->name); + _asn1_str_cat (name2, sizeof (name2), "."); + _asn1_str_cat (name2, sizeof (name2), p2->value); + strcpy (_asn1_identifierMissing, p2->value); + p2 = asn1_find_node (node, name2); + if (!p2 || (type_field (p2->type) != TYPE_OBJECT_ID) || + !(p2->type & CONST_ASSIGN)) + return ASN1_IDENTIFIER_NOT_FOUND; + else + _asn1_identifierMissing[0] = 0; + } + } + else if ((type_field (p->type) == TYPE_OBJECT_ID) && + (p->type & CONST_ASSIGN)) + { + p2 = p->down; + if (p2 && (type_field (p2->type) == TYPE_CONSTANT)) + { + if (p2->value && !isdigit (p2->value[0])) + { + _asn1_str_cpy (name2, sizeof (name2), node->name); + _asn1_str_cat (name2, sizeof (name2), "."); + _asn1_str_cat (name2, sizeof (name2), p2->value); + strcpy (_asn1_identifierMissing, p2->value); + p2 = asn1_find_node (node, name2); + if (!p2 || (type_field (p2->type) != TYPE_OBJECT_ID) || + !(p2->type & CONST_ASSIGN)) + return ASN1_IDENTIFIER_NOT_FOUND; + else + _asn1_identifierMissing[0] = 0; + } + } + } + + if (p->down) + { + p = p->down; + } + else if (p->right) + p = p->right; + else + { + while (1) + { + p = _asn1_find_up (p); + if (p == node) + { + p = NULL; + break; + } + if (p->right) + { + p = p->right; + break; + } + } + } + } + + return ASN1_SUCCESS; +} + + +/******************************************************************/ +/* Function : _asn1_set_default_tag */ +/* Description: sets the default IMPLICIT or EXPLICIT property in */ +/* the tagged elements that don't have this declaration. */ +/* Parameters: */ +/* node: pointer to a DEFINITIONS element. */ +/* Return: */ +/* ASN1_ELEMENT_NOT_FOUND if NODE is NULL or not a pointer to */ +/* a DEFINITIONS element, */ +/* otherwise ASN1_SUCCESS */ +/******************************************************************/ +asn1_retCode +_asn1_set_default_tag (ASN1_TYPE node) +{ + node_asn *p; + + if ((node == NULL) || (type_field (node->type) != TYPE_DEFINITIONS)) + return ASN1_ELEMENT_NOT_FOUND; + + p = node; + while (p) + { + if ((type_field (p->type) == TYPE_TAG) && + !(p->type & CONST_EXPLICIT) && !(p->type & CONST_IMPLICIT)) + { + if (node->type & CONST_EXPLICIT) + p->type |= CONST_EXPLICIT; + else + p->type |= CONST_IMPLICIT; + } + + if (p->down) + { + p = p->down; + } + else if (p->right) + p = p->right; + else + { + while (1) + { + p = _asn1_find_up (p); + if (p == node) + { + p = NULL; + break; + } + if (p->right) + { + p = p->right; + break; + } + } + } + } + + return ASN1_SUCCESS; +} + + + +static const char * +parse_version_number (const char *s, int *number) +{ + int val = 0; + + if (*s == '0' && isdigit (s[1])) + return NULL; /* leading zeros are not allowed */ + for (; isdigit (*s); s++) + { + val *= 10; + val += *s - '0'; + } + *number = val; + return val < 0 ? NULL : s; +} + +/* The parse version functions were copied from libgcrypt. + */ +static const char * +parse_version_string (const char *s, int *major, int *minor, int *micro) +{ + s = parse_version_number (s, major); + if (!s || *s != '.') + return NULL; + s++; + s = parse_version_number (s, minor); + if (!s) + return NULL; + if (*s != '.') + { + *micro = 0; + return s; + } + s++; + s = parse_version_number (s, micro); + if (!s) + return NULL; + return s; /* patchlevel */ +} + +/** + * asn1_check_version - check for library version + * @req_version: Required version number, or NULL. + * + * Check that the the version of the library is at minimum the + * requested one and return the version string; return %NULL if the + * condition is not satisfied. If a %NULL is passed to this function, + * no check is done, but the version string is simply returned. + * + * See %LIBTASN1_VERSION for a suitable @req_version string. + * + * Return value: Version string of run-time library, or %NULL if the + * run-time library does not meet the required version number. + */ +const char * +asn1_check_version (const char *req_version) +{ + const char *ver = LIBTASN1_VERSION; + int my_major, my_minor, my_micro; + int rq_major, rq_minor, rq_micro; + const char *my_plvl, *rq_plvl; + + if (!req_version) + return ver; + + my_plvl = parse_version_string (ver, &my_major, &my_minor, &my_micro); + if (!my_plvl) + return NULL; /* very strange our own version is bogus */ + rq_plvl = parse_version_string (req_version, &rq_major, &rq_minor, + &rq_micro); + if (!rq_plvl) + return NULL; /* req version string is invalid */ + + if (my_major > rq_major + || (my_major == rq_major && my_minor > rq_minor) + || (my_major == rq_major && my_minor == rq_minor + && my_micro > rq_micro) + || (my_major == rq_major && my_minor == rq_minor + && my_micro == rq_micro && strcmp (my_plvl, rq_plvl) >= 0)) + { + return ver; + } + return NULL; +} diff --git a/src/daemon/https/minitasn1/parser_aux.h b/src/daemon/https/minitasn1/parser_aux.h new file mode 100644 index 00000000..3055510c --- /dev/null +++ b/src/daemon/https/minitasn1/parser_aux.h @@ -0,0 +1,63 @@ + +#ifndef _PARSER_AUX_H +#define _PARSER_AUX_H + + +/***************************************/ +/* Functions used by ASN.1 parser */ +/***************************************/ +node_asn * +_asn1_add_node(unsigned int type); + +node_asn * +_asn1_set_value(node_asn *node,const void *value,unsigned int len); + +node_asn * +_asn1_set_name(node_asn *node,const char *name); + +node_asn * +_asn1_set_right(node_asn *node,node_asn *right); + +node_asn * +_asn1_get_right(node_asn *node); + +node_asn * +_asn1_get_last_right(node_asn *node); + +node_asn * +_asn1_set_down(node_asn *node,node_asn *down); + +char * +_asn1_get_name(node_asn *node); + +node_asn * +_asn1_get_down(node_asn *node); + +node_asn * +_asn1_mod_type(node_asn *node,unsigned int value); + +void +_asn1_remove_node(node_asn *node); + +void _asn1_delete_list(void); + +void _asn1_delete_list_and_nodes(void); + +char * _asn1_ltostr(long v,char *str); + +node_asn * _asn1_find_up(node_asn *node); + +asn1_retCode _asn1_change_integer_value(ASN1_TYPE node); + +asn1_retCode _asn1_expand_object_id(ASN1_TYPE node); + +asn1_retCode _asn1_type_set_config(ASN1_TYPE node); + +asn1_retCode _asn1_check_identifier(ASN1_TYPE node); + +asn1_retCode _asn1_set_default_tag(ASN1_TYPE node); + +#endif + + + diff --git a/src/daemon/https/minitasn1/structure.c b/src/daemon/https/minitasn1/structure.c new file mode 100644 index 00000000..264793c1 --- /dev/null +++ b/src/daemon/https/minitasn1/structure.c @@ -0,0 +1,1221 @@ +/* + * Copyright (C) 2004, 2006, 2007 Free Software Foundation + * Copyright (C) 2002 Fabio Fiorina + * + * This file is part of LIBTASN1. + * + * The LIBTASN1 library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +/*****************************************************/ +/* File: structure.c */ +/* Description: Functions to create and delete an */ +/* ASN1 tree. */ +/*****************************************************/ + + +#include +#include +#include +#include "parser_aux.h" +#include + + +extern char _asn1_identifierMissing[]; + + +/******************************************************/ +/* Function : _asn1_add_node_only */ +/* Description: creates a new NODE_ASN element. */ +/* Parameters: */ +/* type: type of the new element (see TYPE_ */ +/* and CONST_ constants). */ +/* Return: pointer to the new element. */ +/******************************************************/ +node_asn * +_asn1_add_node_only (unsigned int type) +{ + node_asn *punt; + + punt = (node_asn *) _asn1_calloc (1, sizeof (node_asn)); + if (punt == NULL) + return NULL; + + punt->type = type; + + return punt; +} + + +/******************************************************************/ +/* Function : _asn1_find_left */ +/* Description: returns the NODE_ASN element with RIGHT field that*/ +/* points the element NODE. */ +/* Parameters: */ +/* node: NODE_ASN element pointer. */ +/* Return: NULL if not found. */ +/******************************************************************/ +node_asn * +_asn1_find_left (node_asn * node) +{ + if ((node == NULL) || (node->left == NULL) || (node->left->down == node)) + return NULL; + + return node->left; +} + + +asn1_retCode +_asn1_create_static_structure (ASN1_TYPE pointer, char *output_file_name, + char *vector_name) +{ + FILE *file; + node_asn *p; + unsigned long t; + + file = fopen (output_file_name, "w"); + + if (file == NULL) + return ASN1_FILE_NOT_FOUND; + + fprintf (file, "#if HAVE_CONFIG_H\n"); + fprintf (file, "# include \"config.h\"\n"); + fprintf (file, "#endif\n\n"); + + fprintf (file, "#include \n\n"); + + fprintf (file, "extern const ASN1_ARRAY_TYPE %s[]={\n", vector_name); + + p = pointer; + + while (p) + { + fprintf (file, " {"); + + if (p->name) + fprintf (file, "\"%s\",", p->name); + else + fprintf (file, "0,"); + + t = p->type; + if (p->down) + t |= CONST_DOWN; + if (p->right) + t |= CONST_RIGHT; + + fprintf (file, "%lu,", t); + + if (p->value) + fprintf (file, "\"%s\"},\n", p->value); + else + fprintf (file, "0},\n"); + + if (p->down) + { + p = p->down; + } + else if (p->right) + { + p = p->right; + } + else + { + while (1) + { + p = _asn1_find_up (p); + if (p == pointer) + { + p = NULL; + break; + } + if (p->right) + { + p = p->right; + break; + } + } + } + } + + fprintf (file, " {0,0,0}\n};\n"); + + fclose (file); + + return ASN1_SUCCESS; +} + + +/** + * asn1_array2tree - Creates the structures needed to manage the ASN1 definitions. + * @array: specify the array that contains ASN.1 declarations + * @definitions: return the pointer to the structure created by + * *ARRAY ASN.1 declarations + * @errorDescription: return the error description. + * + * Creates the structures needed to manage the ASN.1 definitions. + * @array is a vector created by asn1_parser2array(). + * + * Returns: + * + * ASN1_SUCCESS: Structure created correctly. + * + * ASN1_ELEMENT_NOT_EMPTY: *@definitions not ASN1_TYPE_EMPTY. + * + * ASN1_IDENTIFIER_NOT_FOUND: In the file there is an identifier that + * is not defined (see @errorDescription for more information). + * + * ASN1_ARRAY_ERROR: The array pointed by @array is wrong. + **/ +asn1_retCode +asn1_array2tree (const ASN1_ARRAY_TYPE * array, ASN1_TYPE * definitions, + char *errorDescription) +{ + node_asn *p, *p_last = NULL; + unsigned long k; + int move; + asn1_retCode result; + + + if (*definitions != ASN1_TYPE_EMPTY) + return ASN1_ELEMENT_NOT_EMPTY; + + move = UP; + + k = 0; + while (array[k].value || array[k].type || array[k].name) + { + p = _asn1_add_node (array[k].type & (~CONST_DOWN)); + if (array[k].name) + _asn1_set_name (p, array[k].name); + if (array[k].value) + _asn1_set_value (p, array[k].value, strlen (array[k].value) + 1); + + if (*definitions == NULL) + *definitions = p; + + if (move == DOWN) + _asn1_set_down (p_last, p); + else if (move == RIGHT) + _asn1_set_right (p_last, p); + + p_last = p; + + if (array[k].type & CONST_DOWN) + move = DOWN; + else if (array[k].type & CONST_RIGHT) + move = RIGHT; + else + { + while (1) + { + if (p_last == *definitions) + break; + + p_last = _asn1_find_up (p_last); + + if (p_last == NULL) + break; + + if (p_last->type & CONST_RIGHT) + { + p_last->type &= ~CONST_RIGHT; + move = RIGHT; + break; + } + } /* while */ + } + k++; + } /* while */ + + if (p_last == *definitions) + { + result = _asn1_check_identifier (*definitions); + if (result == ASN1_SUCCESS) + { + _asn1_change_integer_value (*definitions); + _asn1_expand_object_id (*definitions); + } + } + else + { + result = ASN1_ARRAY_ERROR; + } + + if (errorDescription != NULL) + { + if (result == ASN1_IDENTIFIER_NOT_FOUND) + { + Estrcpy (errorDescription, ":: identifier '"); + Estrcat (errorDescription, _asn1_identifierMissing); + Estrcat (errorDescription, "' not found"); + } + else + errorDescription[0] = 0; + } + + if (result != ASN1_SUCCESS) + { + _asn1_delete_list_and_nodes (); + *definitions = ASN1_TYPE_EMPTY; + } + else + _asn1_delete_list (); + + return result; +} + +/** + * asn1_delete_structure - Deletes the structure pointed by *ROOT. + * @structure: pointer to the structure that you want to delete. + * + * Deletes the structure *@structure. At the end, *@structure is set + * to ASN1_TYPE_EMPTY. + * + * Returns: + * + * ASN1_SUCCESS: Everything OK. + * + * ASN1_ELEMENT_NOT_FOUND: *@structure was ASN1_TYPE_EMPTY. + * + **/ +asn1_retCode +asn1_delete_structure (ASN1_TYPE * structure) +{ + node_asn *p, *p2, *p3; + + if (*structure == ASN1_TYPE_EMPTY) + return ASN1_ELEMENT_NOT_FOUND; + + p = *structure; + while (p) + { + if (p->down) + { + p = p->down; + } + else + { /* no down */ + p2 = p->right; + if (p != *structure) + { + p3 = _asn1_find_up (p); + _asn1_set_down (p3, p2); + _asn1_remove_node (p); + p = p3; + } + else + { /* p==root */ + p3 = _asn1_find_left (p); + if (!p3) + { + p3 = _asn1_find_up (p); + if (p3) + _asn1_set_down (p3, p2); + else + { + if (p->right) + p->right->left = NULL; + } + } + else + _asn1_set_right (p3, p2); + _asn1_remove_node (p); + p = NULL; + } + } + } + + *structure = ASN1_TYPE_EMPTY; + return ASN1_SUCCESS; +} + + + +/** + * asn1_delete_element - Deletes the element of a structure. + * @structure: pointer to the structure that contains the element you + * want to delete. + * @element_name: element's name you want to delete. + * + * Deletes the element named *@element_name inside *@structure. + * + * Returns: + * + * ASN1_SUCCESS: Everything OK. + * + * ASN1_ELEMENT_NOT_FOUND: The name element was not found. + * + **/ +asn1_retCode +asn1_delete_element (ASN1_TYPE structure, const char *element_name) +{ + node_asn *p2, *p3, *source_node; + + source_node = asn1_find_node (structure, element_name); + + if (source_node == ASN1_TYPE_EMPTY) + return ASN1_ELEMENT_NOT_FOUND; + + p2 = source_node->right; + p3 = _asn1_find_left (source_node); + if (!p3) + { + p3 = _asn1_find_up (source_node); + if (p3) + _asn1_set_down (p3, p2); + else if (source_node->right) + source_node->right->left = NULL; + } + else + _asn1_set_right (p3, p2); + + return asn1_delete_structure (&source_node); +} + +node_asn * +_asn1_copy_structure3 (node_asn * source_node) +{ + node_asn *dest_node, *p_s, *p_d, *p_d_prev; + int move; + + if (source_node == NULL) + return NULL; + + dest_node = _asn1_add_node_only (source_node->type); + + p_s = source_node; + p_d = dest_node; + + move = DOWN; + + do + { + if (move != UP) + { + if (p_s->name) + _asn1_set_name (p_d, p_s->name); + if (p_s->value) + _asn1_set_value (p_d, p_s->value, p_s->value_len); + move = DOWN; + } + else + move = RIGHT; + + if (move == DOWN) + { + if (p_s->down) + { + p_s = p_s->down; + p_d_prev = p_d; + p_d = _asn1_add_node_only (p_s->type); + _asn1_set_down (p_d_prev, p_d); + } + else + move = RIGHT; + } + + if (p_s == source_node) + break; + + if (move == RIGHT) + { + if (p_s->right) + { + p_s = p_s->right; + p_d_prev = p_d; + p_d = _asn1_add_node_only (p_s->type); + _asn1_set_right (p_d_prev, p_d); + } + else + move = UP; + } + if (move == UP) + { + p_s = _asn1_find_up (p_s); + p_d = _asn1_find_up (p_d); + } + } + while (p_s != source_node); + + return dest_node; +} + + +node_asn * +_asn1_copy_structure2 (node_asn * root, const char *source_name) +{ + node_asn *source_node; + + source_node = asn1_find_node (root, source_name); + + return _asn1_copy_structure3 (source_node); + +} + + +asn1_retCode +_asn1_type_choice_config (node_asn * node) +{ + node_asn *p, *p2, *p3, *p4; + int move, tlen; + + if (node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + p = node; + move = DOWN; + + while (!((p == node) && (move == UP))) + { + if (move != UP) + { + if ((type_field (p->type) == TYPE_CHOICE) && (p->type & CONST_TAG)) + { + p2 = p->down; + while (p2) + { + if (type_field (p2->type) != TYPE_TAG) + { + p2->type |= CONST_TAG; + p3 = _asn1_find_left (p2); + while (p3) + { + if (type_field (p3->type) == TYPE_TAG) + { + p4 = _asn1_add_node_only (p3->type); + tlen = strlen (p3->value); + if (tlen > 0) + _asn1_set_value (p4, p3->value, tlen + 1); + _asn1_set_right (p4, p2->down); + _asn1_set_down (p2, p4); + } + p3 = _asn1_find_left (p3); + } + } + p2 = p2->right; + } + p->type &= ~(CONST_TAG); + p2 = p->down; + while (p2) + { + p3 = p2->right; + if (type_field (p2->type) == TYPE_TAG) + asn1_delete_structure (&p2); + p2 = p3; + } + } + move = DOWN; + } + else + move = RIGHT; + + if (move == DOWN) + { + if (p->down) + p = p->down; + else + move = RIGHT; + } + + if (p == node) + { + move = UP; + continue; + } + + if (move == RIGHT) + { + if (p->right) + p = p->right; + else + move = UP; + } + if (move == UP) + p = _asn1_find_up (p); + } + + return ASN1_SUCCESS; +} + + +asn1_retCode +_asn1_expand_identifier (node_asn ** node, node_asn * root) +{ + node_asn *p, *p2, *p3; + char name2[MAX_NAME_SIZE + 2]; + int move; + + if (node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + p = *node; + move = DOWN; + + while (!((p == *node) && (move == UP))) + { + if (move != UP) + { + if (type_field (p->type) == TYPE_IDENTIFIER) + { + _asn1_str_cpy (name2, sizeof (name2), root->name); + _asn1_str_cat (name2, sizeof (name2), "."); + _asn1_str_cat (name2, sizeof (name2), p->value); + p2 = _asn1_copy_structure2 (root, name2); + if (p2 == NULL) + { + return ASN1_IDENTIFIER_NOT_FOUND; + } + _asn1_set_name (p2, p->name); + p2->right = p->right; + p2->left = p->left; + if (p->right) + p->right->left = p2; + p3 = p->down; + if (p3) + { + while (p3->right) + p3 = p3->right; + _asn1_set_right (p3, p2->down); + _asn1_set_down (p2, p->down); + } + + p3 = _asn1_find_left (p); + if (p3) + _asn1_set_right (p3, p2); + else + { + p3 = _asn1_find_up (p); + if (p3) + _asn1_set_down (p3, p2); + else + { + p2->left = NULL; + } + } + + if (p->type & CONST_SIZE) + p2->type |= CONST_SIZE; + if (p->type & CONST_TAG) + p2->type |= CONST_TAG; + if (p->type & CONST_OPTION) + p2->type |= CONST_OPTION; + if (p->type & CONST_DEFAULT) + p2->type |= CONST_DEFAULT; + if (p->type & CONST_SET) + p2->type |= CONST_SET; + if (p->type & CONST_NOT_USED) + p2->type |= CONST_NOT_USED; + + if (p == *node) + *node = p2; + _asn1_remove_node (p); + p = p2; + move = DOWN; + continue; + } + move = DOWN; + } + else + move = RIGHT; + + if (move == DOWN) + { + if (p->down) + p = p->down; + else + move = RIGHT; + } + + if (p == *node) + { + move = UP; + continue; + } + + if (move == RIGHT) + { + if (p->right) + p = p->right; + else + move = UP; + } + if (move == UP) + p = _asn1_find_up (p); + } + + return ASN1_SUCCESS; +} + + +/** + * asn1_create_element - Creates a structure of type SOURCE_NAME. + * @definitions: pointer to the structure returned by "parser_asn1" function + * @source_name: the name of the type of the new structure (must be + * inside p_structure). + * @element: pointer to the structure created. + * + * Creates a structure of type @source_name. Example using + * "pkix.asn": + * + * rc = asn1_create_structure(cert_def, "PKIX1.Certificate", + * certptr); + * + * Returns: + * + * ASN1_SUCCESS: Creation OK. + * + * ASN1_ELEMENT_NOT_FOUND: SOURCE_NAME isn't known + **/ +asn1_retCode +asn1_create_element (ASN1_TYPE definitions, const char *source_name, + ASN1_TYPE * element) +{ + node_asn *dest_node; + int res; + + dest_node = _asn1_copy_structure2 (definitions, source_name); + + if (dest_node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + _asn1_set_name (dest_node, ""); + + res = _asn1_expand_identifier (&dest_node, definitions); + _asn1_type_choice_config (dest_node); + + *element = dest_node; + + return res; +} + + +/** + * asn1_print_structure - Prints on the standard output the structure's tree + * @out: pointer to the output file (e.g. stdout). + * @structure: pointer to the structure that you want to visit. + * @name: an element of the structure + * @mode: specify how much of the structure to print, can be + * %ASN1_PRINT_NAME, %ASN1_PRINT_NAME_TYPE, + * %ASN1_PRINT_NAME_TYPE_VALUE, or %ASN1_PRINT_ALL. + * + * Prints on the @out file descriptor the structure's tree starting + * from the @name element inside the structure @structure. + **/ +void +asn1_print_structure (FILE * out, ASN1_TYPE structure, const char *name, + int mode) +{ + node_asn *p, *root; + int k, indent = 0, len, len2, len3; + + if (out == NULL) + return; + + root = asn1_find_node (structure, name); + + if (root == NULL) + return; + + p = root; + while (p) + { + if (mode == ASN1_PRINT_ALL) + { + for (k = 0; k < indent; k++) + fprintf (out, " "); + fprintf (out, "name:"); + if (p->name) + fprintf (out, "%s ", p->name); + else + fprintf (out, "NULL "); + } + else + { + switch (type_field (p->type)) + { + case TYPE_CONSTANT: + case TYPE_TAG: + case TYPE_SIZE: + break; + default: + for (k = 0; k < indent; k++) + fprintf (out, " "); + fprintf (out, "name:"); + if (p->name) + fprintf (out, "%s ", p->name); + else + fprintf (out, "NULL "); + } + } + + if (mode != ASN1_PRINT_NAME) + { + switch (type_field (p->type)) + { + case TYPE_CONSTANT: + if (mode == ASN1_PRINT_ALL) + fprintf (out, "type:CONST"); + break; + case TYPE_TAG: + if (mode == ASN1_PRINT_ALL) + fprintf (out, "type:TAG"); + break; + case TYPE_SIZE: + if (mode == ASN1_PRINT_ALL) + fprintf (out, "type:SIZE"); + break; + case TYPE_DEFAULT: + fprintf (out, "type:DEFAULT"); + break; + case TYPE_NULL: + fprintf (out, "type:NULL"); + break; + case TYPE_IDENTIFIER: + fprintf (out, "type:IDENTIFIER"); + break; + case TYPE_INTEGER: + fprintf (out, "type:INTEGER"); + break; + case TYPE_ENUMERATED: + fprintf (out, "type:ENUMERATED"); + break; + case TYPE_TIME: + fprintf (out, "type:TIME"); + break; + case TYPE_BOOLEAN: + fprintf (out, "type:BOOLEAN"); + break; + case TYPE_SEQUENCE: + fprintf (out, "type:SEQUENCE"); + break; + case TYPE_BIT_STRING: + fprintf (out, "type:BIT_STR"); + break; + case TYPE_OCTET_STRING: + fprintf (out, "type:OCT_STR"); + break; + case TYPE_GENERALSTRING: + fprintf (out, "type:GENERALSTRING"); + break; + case TYPE_SEQUENCE_OF: + fprintf (out, "type:SEQ_OF"); + break; + case TYPE_OBJECT_ID: + fprintf (out, "type:OBJ_ID"); + break; + case TYPE_ANY: + fprintf (out, "type:ANY"); + break; + case TYPE_SET: + fprintf (out, "type:SET"); + break; + case TYPE_SET_OF: + fprintf (out, "type:SET_OF"); + break; + case TYPE_CHOICE: + fprintf (out, "type:CHOICE"); + break; + case TYPE_DEFINITIONS: + fprintf (out, "type:DEFINITIONS"); + break; + default: + break; + } + } + + if ((mode == ASN1_PRINT_NAME_TYPE_VALUE) || (mode == ASN1_PRINT_ALL)) + { + switch (type_field (p->type)) + { + case TYPE_CONSTANT: + if (mode == ASN1_PRINT_ALL) + if (p->value) + fprintf (out, " value:%s", p->value); + break; + case TYPE_TAG: + if (mode == ASN1_PRINT_ALL) + if (p->value) + fprintf (out, " value:%s", p->value); + break; + case TYPE_SIZE: + if (mode == ASN1_PRINT_ALL) + if (p->value) + fprintf (out, " value:%s", p->value); + break; + case TYPE_DEFAULT: + if (p->value) + fprintf (out, " value:%s", p->value); + else if (p->type & CONST_TRUE) + fprintf (out, " value:TRUE"); + else if (p->type & CONST_FALSE) + fprintf (out, " value:FALSE"); + break; + case TYPE_IDENTIFIER: + if (p->value) + fprintf (out, " value:%s", p->value); + break; + case TYPE_INTEGER: + if (p->value) + { + len2 = -1; + len = asn1_get_length_der (p->value, p->value_len, &len2); + fprintf (out, " value:0x"); + if (len > 0) + for (k = 0; k < len; k++) + fprintf (out, "%02x", (p->value)[k + len2]); + } + break; + case TYPE_ENUMERATED: + if (p->value) + { + len2 = -1; + len = asn1_get_length_der (p->value, p->value_len, &len2); + fprintf (out, " value:0x"); + if (len > 0) + for (k = 0; k < len; k++) + fprintf (out, "%02x", (p->value)[k + len2]); + } + break; + case TYPE_TIME: + if (p->value) + fprintf (out, " value:%s", p->value); + break; + case TYPE_BOOLEAN: + if (p->value) + { + if (p->value[0] == 'T') + fprintf (out, " value:TRUE"); + else if (p->value[0] == 'F') + fprintf (out, " value:FALSE"); + } + break; + case TYPE_BIT_STRING: + if (p->value) + { + len2 = -1; + len = asn1_get_length_der (p->value, p->value_len, &len2); + if (len > 0) + { + fprintf (out, " value(%i):", + (len - 1) * 8 - (p->value[len2])); + for (k = 1; k < len; k++) + fprintf (out, "%02x", (p->value)[k + len2]); + } + } + break; + case TYPE_OCTET_STRING: + if (p->value) + { + len2 = -1; + len = asn1_get_length_der (p->value, p->value_len, &len2); + fprintf (out, " value:"); + if (len > 0) + for (k = 0; k < len; k++) + fprintf (out, "%02x", (p->value)[k + len2]); + } + break; + case TYPE_GENERALSTRING: + if (p->value) + { + len2 = -1; + len = asn1_get_length_der (p->value, p->value_len, &len2); + fprintf (out, " value:"); + if (len > 0) + for (k = 0; k < len; k++) + fprintf (out, "%02x", (p->value)[k + len2]); + } + break; + case TYPE_OBJECT_ID: + if (p->value) + fprintf (out, " value:%s", p->value); + break; + case TYPE_ANY: + if (p->value) + { + len3 = -1; + len2 = asn1_get_length_der (p->value, p->value_len, &len3); + fprintf (out, " value:"); + if (len2 > 0) + for (k = 0; k < len2; k++) + fprintf (out, "%02x", (p->value)[k + len3]); + } + break; + case TYPE_SET: + case TYPE_SET_OF: + case TYPE_CHOICE: + case TYPE_DEFINITIONS: + case TYPE_SEQUENCE_OF: + case TYPE_SEQUENCE: + case TYPE_NULL: + break; + default: + break; + } + } + + if (mode == ASN1_PRINT_ALL) + { + if (p->type & 0x1FFFFF00) + { + fprintf (out, " attr:"); + if (p->type & CONST_UNIVERSAL) + fprintf (out, "UNIVERSAL,"); + if (p->type & CONST_PRIVATE) + fprintf (out, "PRIVATE,"); + if (p->type & CONST_APPLICATION) + fprintf (out, "APPLICATION,"); + if (p->type & CONST_EXPLICIT) + fprintf (out, "EXPLICIT,"); + if (p->type & CONST_IMPLICIT) + fprintf (out, "IMPLICIT,"); + if (p->type & CONST_TAG) + fprintf (out, "TAG,"); + if (p->type & CONST_DEFAULT) + fprintf (out, "DEFAULT,"); + if (p->type & CONST_TRUE) + fprintf (out, "TRUE,"); + if (p->type & CONST_FALSE) + fprintf (out, "FALSE,"); + if (p->type & CONST_LIST) + fprintf (out, "LIST,"); + if (p->type & CONST_MIN_MAX) + fprintf (out, "MIN_MAX,"); + if (p->type & CONST_OPTION) + fprintf (out, "OPTION,"); + if (p->type & CONST_1_PARAM) + fprintf (out, "1_PARAM,"); + if (p->type & CONST_SIZE) + fprintf (out, "SIZE,"); + if (p->type & CONST_DEFINED_BY) + fprintf (out, "DEF_BY,"); + if (p->type & CONST_GENERALIZED) + fprintf (out, "GENERALIZED,"); + if (p->type & CONST_UTC) + fprintf (out, "UTC,"); + if (p->type & CONST_SET) + fprintf (out, "SET,"); + if (p->type & CONST_NOT_USED) + fprintf (out, "NOT_USED,"); + if (p->type & CONST_ASSIGN) + fprintf (out, "ASSIGNMENT,"); + } + } + + if (mode == ASN1_PRINT_ALL) + { + fprintf (out, "\n"); + } + else + { + switch (type_field (p->type)) + { + case TYPE_CONSTANT: + case TYPE_TAG: + case TYPE_SIZE: + break; + default: + fprintf (out, "\n"); + } + } + + if (p->down) + { + p = p->down; + indent += 2; + } + else if (p == root) + { + p = NULL; + break; + } + else if (p->right) + p = p->right; + else + { + while (1) + { + p = _asn1_find_up (p); + if (p == root) + { + p = NULL; + break; + } + indent -= 2; + if (p->right) + { + p = p->right; + break; + } + } + } + } +} + + + +/** + * asn1_number_of_elements - Counts the number of elements of a structure. + * @element: pointer to the root of an ASN1 structure. + * @name: the name of a sub-structure of ROOT. + * @num: pointer to an integer where the result will be stored + * + * Counts the number of elements of a sub-structure called NAME with + * names equal to "?1","?2", ... + * + * Returns: + * + * ASN1_SUCCESS: Creation OK. + * + * ASN1_ELEMENT_NOT_FOUND: NAME isn't known. + * + * ASN1_GENERIC_ERROR: Pointer num equal to NULL. + * + **/ +asn1_retCode +asn1_number_of_elements (ASN1_TYPE element, const char *name, int *num) +{ + node_asn *node, *p; + + if (num == NULL) + return ASN1_GENERIC_ERROR; + + *num = 0; + + node = asn1_find_node (element, name); + if (node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + p = node->down; + + while (p) + { + if ((p->name) && (p->name[0] == '?')) + (*num)++; + p = p->right; + } + + return ASN1_SUCCESS; +} + + +/** + * asn1_find_structure_from_oid - Locate structure defined by a specific OID. + * @definitions: ASN1 definitions + * @oidValue: value of the OID to search (e.g. "1.2.3.4"). + * + * Search the structure that is defined just after an OID definition. + * + * Returns: NULL when OIDVALUE not found, otherwise the pointer to a + * constant string that contains the element name defined just + * after the OID. + * + **/ +const char * +asn1_find_structure_from_oid (ASN1_TYPE definitions, const char *oidValue) +{ + char definitionsName[MAX_NAME_SIZE], name[2 * MAX_NAME_SIZE + 1]; + char value[MAX_NAME_SIZE]; + ASN1_TYPE p; + int len; + asn1_retCode result; + + if ((definitions == ASN1_TYPE_EMPTY) || (oidValue == NULL)) + return NULL; /* ASN1_ELEMENT_NOT_FOUND; */ + + + strcpy (definitionsName, definitions->name); + strcat (definitionsName, "."); + + /* search the OBJECT_ID into definitions */ + p = definitions->down; + while (p) + { + if ((type_field (p->type) == TYPE_OBJECT_ID) && + (p->type & CONST_ASSIGN)) + { + strcpy (name, definitionsName); + strcat (name, p->name); + + len = MAX_NAME_SIZE; + result = asn1_read_value (definitions, name, value, &len); + + if ((result == ASN1_SUCCESS) && (!strcmp (oidValue, value))) + { + p = p->right; + if (p == NULL) /* reach the end of ASN1 definitions */ + return NULL; /* ASN1_ELEMENT_NOT_FOUND; */ + + return p->name; + } + } + p = p->right; + } + + return NULL; /* ASN1_ELEMENT_NOT_FOUND; */ +} + +/** + * asn1_copy_node: + * @dst: Destination ASN1_TYPE node. + * @dst_name: Field name in destination node. + * @src: Source ASN1_TYPE node. + * @src_name: Field name in source node. + * + * Create a deep copy of a ASN1_TYPE variable. + * + * Return value: Return ASN1_SUCCESS on success. + **/ +asn1_retCode +asn1_copy_node (ASN1_TYPE dst, const char *dst_name, + ASN1_TYPE src, const char *src_name) +{ +/* FIXME: rewrite using copy_structure(). + * It seems quite hard to do. + */ + int result; + ASN1_TYPE dst_node; + void *data = NULL; + int size = 0; + + result = asn1_der_coding (src, src_name, NULL, &size, NULL); + if (result != ASN1_MEM_ERROR) + return result; + + data = _asn1_malloc (size); + if (data == NULL) + return ASN1_MEM_ERROR; + + result = asn1_der_coding (src, src_name, data, &size, NULL); + if (result != ASN1_SUCCESS) + { + _asn1_free (data); + return result; + } + + dst_node = asn1_find_node (dst, dst_name); + if (dst_node == NULL) + { + _asn1_free (data); + return ASN1_ELEMENT_NOT_FOUND; + } + + result = asn1_der_decoding (&dst_node, data, size, NULL); + + _asn1_free (data); + + return result; +} diff --git a/src/daemon/https/minitasn1/structure.h b/src/daemon/https/minitasn1/structure.h new file mode 100644 index 00000000..4c78391e --- /dev/null +++ b/src/daemon/https/minitasn1/structure.h @@ -0,0 +1,23 @@ + +/*************************************************/ +/* File: structure.h */ +/* Description: list of exported object by */ +/* "structure.c" */ +/*************************************************/ + +#ifndef _STRUCTURE_H +#define _STRUCTURE_H + +asn1_retCode _asn1_create_static_structure(node_asn *pointer, + char* output_file_name,char *vector_name); + +node_asn* _asn1_copy_structure3(node_asn *source_node); + +node_asn* _asn1_copy_structure2(node_asn *root,const char *source_name); + +node_asn * _asn1_add_node_only(unsigned int type); + +node_asn * _asn1_find_left(node_asn *node); + +#endif + diff --git a/src/daemon/https/opencdk/Makefile.am b/src/daemon/https/opencdk/Makefile.am new file mode 100644 index 00000000..b69a151f --- /dev/null +++ b/src/daemon/https/opencdk/Makefile.am @@ -0,0 +1,12 @@ +AM_CPPFLAGS = -I$(top_srcdir)/lib \ +-I$(top_srcdir)/lgl \ +-I$(GCRYPT_CPPFLAGS) + +noinst_LTLIBRARIES = libopencdk.la + +libopencdk_la_LDFLAGS = -lgcrypt + +libopencdk_la_SOURCES = armor.c filters.h main.c seskey.c types.h \ + cipher.c kbnode.c main.h packet.h dummy.c sig-check.c verify.c \ + compress.c keydb.c misc.c pubkey.c stream.c write-packet.c \ + context.h literal.c new-packet.c read-packet.c stream.h opencdk.h diff --git a/src/daemon/https/opencdk/README b/src/daemon/https/opencdk/README new file mode 100644 index 00000000..2cf780e0 --- /dev/null +++ b/src/daemon/https/opencdk/README @@ -0,0 +1,5 @@ +This is a stripped down mirror of the files in OpenCDK +src/. To avoid to link proc-packets.c, dummy.c is included. + +In Makefile.am libminiopencdk_la_SOURCES contains the list +of all needed files. diff --git a/src/daemon/https/opencdk/armor.c b/src/daemon/https/opencdk/armor.c new file mode 100644 index 00000000..e95f7eb1 --- /dev/null +++ b/src/daemon/https/opencdk/armor.c @@ -0,0 +1,763 @@ +/* armor.c - Armor filters + * Copyright (C) 2001, 2002, 2003, 2007 Timo Schulz + * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. + * + * This file is part of OpenCDK. + * + * OpenCDK 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. + * + * OpenCDK 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. + * + * ChangeLog for basic BASE64 code (base64_encode, base64_decode): + * Original author: Eric S. Raymond (Fetchmail) + * Heavily modified by Brendan Cully (Mutt) + * Modify the code for generic use by Timo Schulz + */ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include +#include +#include +#include + +#include "opencdk.h" +#include "main.h" +#include "filters.h" + +#ifdef __MINGW32__ +# define LF "\r\n" +#else +# define LF "\n" +#endif + +#define CRCINIT 0xB704CE + +#define BAD -1 +#define b64val(c) index64[(unsigned int)(c)] + +static u32 crc_table[] = { + 0x000000, 0x864CFB, 0x8AD50D, 0x0C99F6, 0x93E6E1, 0x15AA1A, 0x1933EC, + 0x9F7F17, + 0xA18139, 0x27CDC2, 0x2B5434, 0xAD18CF, 0x3267D8, 0xB42B23, 0xB8B2D5, + 0x3EFE2E, + 0xC54E89, 0x430272, 0x4F9B84, 0xC9D77F, 0x56A868, 0xD0E493, 0xDC7D65, + 0x5A319E, + 0x64CFB0, 0xE2834B, 0xEE1ABD, 0x685646, 0xF72951, 0x7165AA, 0x7DFC5C, + 0xFBB0A7, + 0x0CD1E9, 0x8A9D12, 0x8604E4, 0x00481F, 0x9F3708, 0x197BF3, 0x15E205, + 0x93AEFE, + 0xAD50D0, 0x2B1C2B, 0x2785DD, 0xA1C926, 0x3EB631, 0xB8FACA, 0xB4633C, + 0x322FC7, + 0xC99F60, 0x4FD39B, 0x434A6D, 0xC50696, 0x5A7981, 0xDC357A, 0xD0AC8C, + 0x56E077, + 0x681E59, 0xEE52A2, 0xE2CB54, 0x6487AF, 0xFBF8B8, 0x7DB443, 0x712DB5, + 0xF7614E, + 0x19A3D2, 0x9FEF29, 0x9376DF, 0x153A24, 0x8A4533, 0x0C09C8, 0x00903E, + 0x86DCC5, + 0xB822EB, 0x3E6E10, 0x32F7E6, 0xB4BB1D, 0x2BC40A, 0xAD88F1, 0xA11107, + 0x275DFC, + 0xDCED5B, 0x5AA1A0, 0x563856, 0xD074AD, 0x4F0BBA, 0xC94741, 0xC5DEB7, + 0x43924C, + 0x7D6C62, 0xFB2099, 0xF7B96F, 0x71F594, 0xEE8A83, 0x68C678, 0x645F8E, + 0xE21375, + 0x15723B, 0x933EC0, 0x9FA736, 0x19EBCD, 0x8694DA, 0x00D821, 0x0C41D7, + 0x8A0D2C, + 0xB4F302, 0x32BFF9, 0x3E260F, 0xB86AF4, 0x2715E3, 0xA15918, 0xADC0EE, + 0x2B8C15, + 0xD03CB2, 0x567049, 0x5AE9BF, 0xDCA544, 0x43DA53, 0xC596A8, 0xC90F5E, + 0x4F43A5, + 0x71BD8B, 0xF7F170, 0xFB6886, 0x7D247D, 0xE25B6A, 0x641791, 0x688E67, + 0xEEC29C, + 0x3347A4, 0xB50B5F, 0xB992A9, 0x3FDE52, 0xA0A145, 0x26EDBE, 0x2A7448, + 0xAC38B3, + 0x92C69D, 0x148A66, 0x181390, 0x9E5F6B, 0x01207C, 0x876C87, 0x8BF571, + 0x0DB98A, + 0xF6092D, 0x7045D6, 0x7CDC20, 0xFA90DB, 0x65EFCC, 0xE3A337, 0xEF3AC1, + 0x69763A, + 0x578814, 0xD1C4EF, 0xDD5D19, 0x5B11E2, 0xC46EF5, 0x42220E, 0x4EBBF8, + 0xC8F703, + 0x3F964D, 0xB9DAB6, 0xB54340, 0x330FBB, 0xAC70AC, 0x2A3C57, 0x26A5A1, + 0xA0E95A, + 0x9E1774, 0x185B8F, 0x14C279, 0x928E82, 0x0DF195, 0x8BBD6E, 0x872498, + 0x016863, + 0xFAD8C4, 0x7C943F, 0x700DC9, 0xF64132, 0x693E25, 0xEF72DE, 0xE3EB28, + 0x65A7D3, + 0x5B59FD, 0xDD1506, 0xD18CF0, 0x57C00B, 0xC8BF1C, 0x4EF3E7, 0x426A11, + 0xC426EA, + 0x2AE476, 0xACA88D, 0xA0317B, 0x267D80, 0xB90297, 0x3F4E6C, 0x33D79A, + 0xB59B61, + 0x8B654F, 0x0D29B4, 0x01B042, 0x87FCB9, 0x1883AE, 0x9ECF55, 0x9256A3, + 0x141A58, + 0xEFAAFF, 0x69E604, 0x657FF2, 0xE33309, 0x7C4C1E, 0xFA00E5, 0xF69913, + 0x70D5E8, + 0x4E2BC6, 0xC8673D, 0xC4FECB, 0x42B230, 0xDDCD27, 0x5B81DC, 0x57182A, + 0xD154D1, + 0x26359F, 0xA07964, 0xACE092, 0x2AAC69, 0xB5D37E, 0x339F85, 0x3F0673, + 0xB94A88, + 0x87B4A6, 0x01F85D, 0x0D61AB, 0x8B2D50, 0x145247, 0x921EBC, 0x9E874A, + 0x18CBB1, + 0xE37B16, 0x6537ED, 0x69AE1B, 0xEFE2E0, 0x709DF7, 0xF6D10C, 0xFA48FA, + 0x7C0401, + 0x42FA2F, 0xC4B6D4, 0xC82F22, 0x4E63D9, 0xD11CCE, 0x575035, 0x5BC9C3, + 0xDD8538 +}; + +static const char *armor_begin[] = { + "BEGIN PGP MESSAGE", + "BEGIN PGP PUBLIC KEY BLOCK", + "BEGIN PGP PRIVATE KEY BLOCK", + "BEGIN PGP SIGNATURE", + NULL +}; + +static const char *armor_end[] = { + "END PGP MESSAGE", + "END PGP PUBLIC KEY BLOCK", + "END PGP PRIVATE KEY BLOCK", + "END PGP SIGNATURE", + NULL +}; + +static const char *valid_headers[] = { + "Comment", + "Version", + "MessageID", + "Hash", + "Charset", + NULL +}; + +static char b64chars[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +static int index64[128] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, + -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1 +}; + + +/* encode a raw binary buffer to a null-terminated base64 strings */ +static int +base64_encode (char *out, const byte * in, size_t len, size_t olen) +{ + if (!out || !in) + return CDK_Inv_Value; + + while (len >= 3 && olen > 10) + { + *out++ = b64chars[in[0] >> 2]; + *out++ = b64chars[((in[0] << 4) & 0x30) | (in[1] >> 4)]; + *out++ = b64chars[((in[1] << 2) & 0x3c) | (in[2] >> 6)]; + *out++ = b64chars[in[2] & 0x3f]; + olen -= 4; + len -= 3; + in += 3; + } + + /* clean up remainder */ + if (len > 0 && olen > 4) + { + byte fragment = 0; + *out++ = b64chars[in[0] >> 2]; + fragment = (in[0] << 4) & 0x30; + if (len > 1) + fragment |= in[1] >> 4; + *out++ = b64chars[fragment]; + *out++ = (len < 2) ? '=' : b64chars[(in[1] << 2) & 0x3c]; + *out++ = '='; + } + *out = '\0'; + return 0; +} + + +/* Convert '\0'-terminated base64 string to raw byte buffer. + Returns length of returned buffer, or -1 on error. */ +static int +base64_decode (byte * out, const char *in) +{ + size_t len; + byte digit1, digit2, digit3, digit4; + + if (!out || !in) + return -1; + + len = 0; + do + { + digit1 = in[0]; + if (digit1 > 127 || b64val (digit1) == BAD) + return -1; + digit2 = in[1]; + if (digit2 > 127 || b64val (digit2) == BAD) + return -1; + digit3 = in[2]; + if (digit3 > 127 || ((digit3 != '=') && (b64val (digit3) == BAD))) + return -1; + digit4 = in[3]; + if (digit4 > 127 || ((digit4 != '=') && (b64val (digit4) == BAD))) + return -1; + in += 4; + + /* digits are already sanity-checked */ + *out++ = (b64val (digit1) << 2) | (b64val (digit2) >> 4); + len++; + if (digit3 != '=') + { + *out++ = ((b64val (digit2) << 4) & 0xf0) | (b64val (digit3) >> 2); + len++; + if (digit4 != '=') + { + *out++ = ((b64val (digit3) << 6) & 0xc0) | b64val (digit4); + len++; + } + } + } + while (*in && digit4 != '='); + + return len; +} + + +/* Return the compression algorithm in @r_zipalgo. + If the parameter is not set after execution, + the stream is not compressed. */ +static int +compress_get_algo (cdk_stream_t inp, int *r_zipalgo) +{ + byte plain[512]; + char buf[128]; + int nread, pkttype; + + *r_zipalgo = 0; + cdk_stream_seek (inp, 0); + while (!cdk_stream_eof (inp)) + { + nread = _cdk_stream_gets (inp, buf, DIM (buf) - 1); + if (!nread || nread == -1) + break; + if (nread == 1 && !cdk_stream_eof (inp) + && (nread = _cdk_stream_gets (inp, buf, DIM (buf) - 1)) > 0) + { + base64_decode (plain, buf); + if (!(*plain & 0x80)) + break; + pkttype = *plain & 0x40 ? (*plain & 0x3f) : ((*plain >> 2) & 0xf); + if (pkttype == CDK_PKT_COMPRESSED && r_zipalgo) + { + _cdk_log_debug ("armor compressed (algo=%d)\n", *(plain + 1)); + *r_zipalgo = *(plain + 1); + } + break; + } + } + return 0; +} + + +static int +check_armor (cdk_stream_t inp, int *r_zipalgo) +{ + char buf[4096]; + size_t nread; + int check; + + check = 0; + nread = cdk_stream_read (inp, buf, DIM (buf) - 1); + if (nread > 0) + { + buf[nread] = '\0'; + if (strstr (buf, "-----BEGIN PGP")) + { + compress_get_algo (inp, r_zipalgo); + check = 1; + } + cdk_stream_seek (inp, 0); + } + return check; +} + + +static int +is_armored (int ctb) +{ + int pkttype = 0; + + if (!(ctb & 0x80)) + return 1; /* invalid packet: assume it is armored */ + pkttype = ctb & 0x40 ? (ctb & 0x3f) : ((ctb >> 2) & 0xf); + switch (pkttype) + { + case CDK_PKT_MARKER: + case CDK_PKT_SYMKEY_ENC: + case CDK_PKT_ONEPASS_SIG: + case CDK_PKT_PUBLIC_KEY: + case CDK_PKT_SECRET_KEY: + case CDK_PKT_PUBKEY_ENC: + case CDK_PKT_SIGNATURE: + case CDK_PKT_LITERAL: + case CDK_PKT_COMPRESSED: + case CDK_PKT_ENCRYPTED: + return 0; /* seems to be a regular packet: not armored */ + } + return 1; +} + + +static u32 +update_crc (u32 crc, const byte * buf, size_t buflen) +{ + int j; + + if (!crc) + crc = CRCINIT; + + for (j = 0; j < buflen; j++) + crc = (crc << 8) ^ crc_table[0xff & ((crc >> 16) ^ buf[j])]; + crc &= 0xffffff; + return crc; +} + + +static cdk_error_t +armor_encode (void *opaque, FILE * in, FILE * out) +{ + armor_filter_t *afx = opaque; + struct stat statbuf; + char crcbuf[5], buf[128], raw[49]; + byte crcbuf2[3]; + size_t nread = 0; + const char *lf; + + if (!afx) + return CDK_Inv_Value; + if (afx->idx < 0 || afx->idx > DIM (armor_begin) || + afx->idx2 < 0 || afx->idx2 > DIM (armor_end)) + return CDK_Inv_Value; + + _cdk_log_debug ("armor filter: encode\n"); + + memset (crcbuf, 0, sizeof (crcbuf)); + + lf = afx->le ? afx->le : LF; + fprintf (out, "-----%s-----%s", armor_begin[afx->idx], lf); + fprintf (out, "Version: OpenPrivacy " PACKAGE_VERSION "%s", lf); + if (afx->hdrlines) + fwrite (afx->hdrlines, 1, strlen (afx->hdrlines), out); + fprintf (out, "%s", lf); + + if (fstat (fileno (in), &statbuf)) + return CDK_General_Error; + + while (!feof (in)) + { + nread = fread (raw, 1, DIM (raw) - 1, in); + if (!nread) + break; + if (ferror (in)) + return CDK_File_Error; + afx->crc = update_crc (afx->crc, (byte *) raw, nread); + base64_encode (buf, (byte *) raw, nread, DIM (buf) - 1); + fprintf (out, "%s%s", buf, lf); + } + + crcbuf2[0] = afx->crc >> 16; + crcbuf2[1] = afx->crc >> 8; + crcbuf2[2] = afx->crc; + crcbuf[0] = b64chars[crcbuf2[0] >> 2]; + crcbuf[1] = b64chars[((crcbuf2[0] << 4) & 0x30) | (crcbuf2[1] >> 4)]; + crcbuf[2] = b64chars[((crcbuf2[1] << 2) & 0x3c) | (crcbuf2[2] >> 6)]; + crcbuf[3] = b64chars[crcbuf2[2] & 0x3f]; + fprintf (out, "=%s%s", crcbuf, lf); + fprintf (out, "-----%s-----%s", armor_end[afx->idx2], lf); + + return 0; +} + + +/** + * cdk_armor_filter_use: + * @inp: the stream to check + * + * Check if the stream contains armored data. + **/ +int +cdk_armor_filter_use (cdk_stream_t inp) +{ + int c, check; + int zipalgo; + + zipalgo = 0; + c = cdk_stream_getc (inp); + if (c == EOF) + return 0; /* EOF, doesn't matter whether armored or not */ + cdk_stream_seek (inp, 0); + check = is_armored (c); + if (check) + { + check = check_armor (inp, &zipalgo); + if (zipalgo) + _cdk_stream_set_compress_algo (inp, zipalgo); + } + return check; +} + + +static int +search_header (const char *buf, const char **array) +{ + const char *s; + int i; + + if (strlen (buf) < 5 || strncmp (buf, "-----", 5)) + return -1; + for (i = 0; (s = array[i]); i++) + { + if (!strncmp (s, buf + 5, strlen (s))) + return i; + } + return -1; +} + + +const char * +_cdk_armor_get_lineend (void) +{ + return LF; +} + + +static cdk_error_t +armor_decode (void *opaque, FILE * in, FILE * out) +{ + armor_filter_t *afx = opaque; + const char *s; + char buf[127]; + byte raw[128], crcbuf[4]; + u32 crc2 = 0; + size_t nread = 0; + int i, pgp_data = 0; + cdk_error_t rc = 0; + + if (!afx) + return CDK_Inv_Value; + + _cdk_log_debug ("armor filter: decode\n"); + + fseek (in, 0, SEEK_SET); + /* Search the begin of the message */ + while (!feof (in) && !pgp_data) + { + s = fgets (buf, DIM (buf) - 1, in); + if (!s) + break; + afx->idx = search_header (buf, armor_begin); + if (afx->idx >= 0) + pgp_data = 1; + } + + if (feof (in) || !pgp_data) + return CDK_Armor_Error; /* no data found */ + + /* Parse header until the empty line is reached */ + while (!feof (in)) + { + s = fgets (buf, DIM (buf) - 1, in); + if (!s) + return CDK_EOF; + if (strlen (s) == strlen (LF)) + { + rc = 0; + break; /* empty line */ + } + /* From RFC2440: OpenPGP should consider improperly formatted Armor + Headers to be corruption of the ASCII Armor. A colon and a single + space separate the key and value. */ + if (!strstr (buf, ": ")) + return CDK_Armor_Error; + rc = CDK_General_Error; + for (i = 0; (s = valid_headers[i]); i++) + { + if (!strncmp (s, buf, strlen (s))) + rc = 0; + } + if (rc) + { + /* From RFC2440: Unknown keys should be reported to the user, + but OpenPGP should continue to process the message. */ + _cdk_log_info ("unknown header: `%s'\n", buf); + rc = 0; + } + } + + /* Read the data body */ + while (!feof (in)) + { + s = fgets (buf, DIM (buf) - 1, in); + if (!s) + break; + buf[strlen (buf) - strlen (LF)] = '\0'; + if (buf[0] == '=' && strlen (s) == 5) + { /* CRC */ + memset (crcbuf, 0, sizeof (crcbuf)); + base64_decode (crcbuf, buf + 1); + crc2 = (crcbuf[0] << 16) | (crcbuf[1] << 8) | crcbuf[2]; + break; /* stop here */ + } + else + { + nread = base64_decode (raw, buf); + if (nread == -1 || nread == 0) + break; + afx->crc = update_crc (afx->crc, raw, nread); + fwrite (raw, 1, nread, out); + } + } + + /* Search the tail of the message */ + s = fgets (buf, DIM (buf) - 1, in); + if (s) + { + buf[strlen (buf) - strlen (LF)] = '\0'; + rc = CDK_General_Error; + afx->idx2 = search_header (buf, armor_end); + if (afx->idx2 >= 0) + rc = 0; + } + + /* This catches error when no tail was found or the header is + different then the tail line. */ + if (rc || afx->idx != afx->idx2) + rc = CDK_Armor_Error; + + afx->crc_okay = (afx->crc == crc2) ? 1 : 0; + if (!afx->crc_okay && !rc) + { + _cdk_log_debug ("file crc=%08lX afx_crc=%08lX\n", crc2, afx->crc); + rc = CDK_Armor_CRC_Error; + } + + return rc; +} + + +/** + * cdk_file_armor: + * @hd: Handle + * @file: Name of the file to protect. + * @output: Output filename. + * + * Protect a file with ASCII armor. + **/ +cdk_error_t +cdk_file_armor (cdk_ctx_t hd, const char *file, const char *output) +{ + cdk_stream_t inp, out; + cdk_error_t rc; + + rc = _cdk_check_args (hd->opt.overwrite, file, output); + if (rc) + return rc; + + rc = cdk_stream_open (file, &inp); + if (rc) + return rc; + + rc = cdk_stream_new (output, &out); + if (rc) + { + cdk_stream_close (inp); + return rc; + } + + cdk_stream_set_armor_flag (out, CDK_ARMOR_MESSAGE); + if (hd->opt.compress) + rc = cdk_stream_set_compress_flag (out, hd->compress.algo, + hd->compress.level); + if (!rc) + rc = cdk_stream_set_literal_flag (out, 0, file); + if (!rc) + rc = cdk_stream_kick_off (inp, out); + if (!rc) + rc = _cdk_stream_get_errno (out); + + cdk_stream_close (out); + cdk_stream_close (inp); + return rc; +} + + +/** + * cdk_file_dearmor: + * @file: Name of the file to unprotect. + * @output: Output filename. + * + * Remove ASCII armor from a file. + **/ +cdk_error_t +cdk_file_dearmor (const char *file, const char *output) +{ + cdk_stream_t inp, out; + cdk_error_t rc; + int zipalgo; + + rc = _cdk_check_args (1, file, output); + if (rc) + return rc; + + rc = cdk_stream_open (file, &inp); + if (rc) + return rc; + + rc = cdk_stream_create (output, &out); + if (rc) + { + cdk_stream_close (inp); + return rc; + } + + if (cdk_armor_filter_use (inp)) + { + rc = cdk_stream_set_literal_flag (inp, 0, NULL); + zipalgo = cdk_stream_is_compressed (inp); + if (zipalgo) + rc = cdk_stream_set_compress_flag (inp, zipalgo, 0); + if (!rc) + rc = cdk_stream_set_armor_flag (inp, 0); + if (!rc) + rc = cdk_stream_kick_off (inp, out); + if (!rc) + rc = _cdk_stream_get_errno (inp); + } + + cdk_stream_close (inp); + cdk_stream_close (out); + return rc; +} + + +int +_cdk_filter_armor (void *opaque, int ctl, FILE * in, FILE * out) +{ + if (ctl == STREAMCTL_READ) + return armor_decode (opaque, in, out); + else if (ctl == STREAMCTL_WRITE) + return armor_encode (opaque, in, out); + else if (ctl == STREAMCTL_FREE) + { + armor_filter_t *afx = opaque; + if (afx) + { + _cdk_log_debug ("free armor filter\n"); + afx->idx = afx->idx2 = 0; + afx->crc = afx->crc_okay = 0; + return 0; + } + } + return CDK_Inv_Mode; +} + + +/** + * cdk_armor_encode_buffer: + * @inbuf: the raw input buffer + * @inlen: raw buffer len + * @outbuf: the destination buffer for the base64 output + * @outlen: destination buffer len + * @nwritten: actual length of the base64 data + * @type: the base64 file type. + * + * Encode the given buffer into base64 format. + **/ +cdk_error_t +cdk_armor_encode_buffer (const byte * inbuf, size_t inlen, + char *outbuf, size_t outlen, + size_t * nwritten, int type) +{ + const char *head, *tail, *le; + byte tempbuf[48]; + char tempout[128]; + size_t pos, off, len, rest; + + if (!inbuf || !nwritten) + return CDK_Inv_Value; + if (type > CDK_ARMOR_SIGNATURE) + return CDK_Inv_Mode; + + head = armor_begin[type]; + tail = armor_end[type]; + le = _cdk_armor_get_lineend (); + pos = strlen (head) + 10 + 2 + 2 + strlen (tail) + 10 + 2 + 5 + 2; + /* The output data is 4/3 times larger, plus a line end for each line. */ + pos += (4 * inlen / 3) + 2 * (4 * inlen / 3 / 64); + + if (outbuf && outlen < pos) + return CDK_Too_Short; + + /* Only return the size of the output. */ + if (!outbuf) + { + *nwritten = pos; + return 0; + } + + pos = 0; + memset (outbuf, 0, outlen); + memcpy (outbuf + pos, "-----", 5); + pos += 5; + memcpy (outbuf + pos, head, strlen (head)); + pos += strlen (head); + memcpy (outbuf + pos, "-----", 5); + pos += 5; + memcpy (outbuf + pos, le, strlen (le)); + pos += strlen (le); + memcpy (outbuf + pos, le, strlen (le)); + pos += strlen (le); + rest = inlen; + for (off = 0; off < inlen;) + { + if (rest > 48) + { + memcpy (tempbuf, inbuf + off, 48); + off += 48; + len = 48; + } + else + { + memcpy (tempbuf, inbuf + off, rest); + off += rest; + len = rest; + } + rest -= len; + base64_encode (tempout, tempbuf, len, DIM (tempout) - 1); + memcpy (outbuf + pos, tempout, strlen (tempout)); + pos += strlen (tempout); + memcpy (outbuf + pos, le, strlen (le)); + pos += strlen (le); + } + + memcpy (outbuf + pos, "-----", 5); + pos += 5; + memcpy (outbuf + pos, tail, strlen (tail)); + pos += strlen (tail); + memcpy (outbuf + pos, "-----", 5); + pos += 5; + memcpy (outbuf + pos, le, strlen (le)); + pos += strlen (le); + *nwritten = pos; + return 0; +} diff --git a/src/daemon/https/opencdk/cipher.c b/src/daemon/https/opencdk/cipher.c new file mode 100644 index 00000000..2ce22eb3 --- /dev/null +++ b/src/daemon/https/opencdk/cipher.c @@ -0,0 +1,528 @@ +/* cipher.c - Cipher filters + * Copyright (C) 2002, 2003, 2007 Timo Schulz + * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation + * + * This file is part of OpenCDK. + * + * OpenCDK 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. + * + * OpenCDK 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. + */ +#ifdef HAVE_CONFIG_H +# include +#endif +#include +#include +#include + +#include "opencdk.h" +#include "main.h" +#include "filters.h" + + +/* The maximal cipher block size in octets. */ +#define MAX_CIPHER_BLKSIZE 16 + + +static off_t +fp_get_length (FILE * fp) +{ + struct stat statbuf; + + if (fstat (fileno (fp), &statbuf)) + return (off_t) - 1; + return statbuf.st_size; +} + + +static cdk_error_t +hash_encode (void *opaque, FILE * in, FILE * out) +{ + md_filter_t *mfx = opaque; + byte buf[BUFSIZE]; + gcry_error_t err; + int nread; + + if (!mfx) + return CDK_Inv_Value; + + _cdk_log_debug ("hash filter: encode algo=%d\n", mfx->digest_algo); + + if (!mfx->md) + { + err = gcry_md_open (&mfx->md, mfx->digest_algo, 0); + if (err) + return map_gcry_error (err); + } + + while (!feof (in)) + { + nread = fread (buf, 1, BUFSIZE, in); + if (!nread) + break; + gcry_md_write (mfx->md, buf, nread); + } + + wipemem (buf, sizeof (buf)); + return 0; +} + + +cdk_error_t +_cdk_filter_hash (void *opaque, int ctl, FILE * in, FILE * out) +{ + if (ctl == STREAMCTL_READ) + return hash_encode (opaque, in, out); + else if (ctl == STREAMCTL_FREE) + { + md_filter_t *mfx = opaque; + if (mfx) + { + _cdk_log_debug ("free hash filter\n"); + gcry_md_close (mfx->md); + mfx->md = NULL; + return 0; + } + } + return CDK_Inv_Mode; +} + + +static cdk_error_t +write_header (cipher_filter_t * cfx, FILE * out) +{ + cdk_pkt_encrypted_t ed; + cdk_packet_t pkt; + cdk_error_t rc; + cdk_dek_t dek = cfx->dek; + byte temp[MAX_CIPHER_BLKSIZE + 2]; + size_t blocksize; + int use_mdc, nprefix; + gcry_error_t err; + + blocksize = gcry_cipher_get_algo_blklen (dek->algo); + if (blocksize < 8 || blocksize > 16) + return CDK_Inv_Algo; + + /* It might be possible the receiver does not understand the MDC + output and thus we offer to supress the MDC packet. */ + use_mdc = dek->use_mdc; + if (blocksize == 8) + use_mdc = 0; + + /* We need to increase the data length because the MDC packet will + be also included. It has a fixed length of 22 octets. */ + if (use_mdc && cfx->datalen) + cfx->datalen += 22; + + cdk_pkt_alloc (&pkt, CDK_PKT_ENCRYPTED_MDC); + ed = pkt->pkt.encrypted; + if (!cfx->blkmode.on) + { + ed->len = cfx->datalen; + ed->extralen = blocksize + 2; + } + else + cfx->blkmode.nleft = DEF_BLOCKSIZE; + + if (use_mdc) + { + ed->mdc_method = GCRY_MD_SHA1; + err = gcry_md_open (&cfx->mdc, GCRY_MD_SHA1, 0); + if (err) + return map_gcry_error (err); + } + + /* When we use partial bodies, the MDC feature or a blocksize + larger than 8, we force the use of the new packet format. */ + if (cfx->blkmode.on || use_mdc || blocksize != 8) + pkt->old_ctb = 0; + else + pkt->old_ctb = 1; + pkt->pkttype = use_mdc ? CDK_PKT_ENCRYPTED_MDC : CDK_PKT_ENCRYPTED; + rc = _cdk_pkt_write_fp (out, pkt); + cdk_pkt_release (pkt); + if (rc) + return rc; + + nprefix = blocksize; + gcry_randomize (temp, nprefix, GCRY_STRONG_RANDOM); + temp[nprefix] = temp[nprefix - 2]; + temp[nprefix + 1] = temp[nprefix - 1]; + err = gcry_cipher_open (&cfx->hd, dek->algo, GCRY_CIPHER_MODE_CFB, + use_mdc ? 0 : GCRY_CIPHER_ENABLE_SYNC); + if (err) + return map_gcry_error (err); + err = gcry_cipher_setiv (cfx->hd, NULL, 0); + if (err) + return map_gcry_error (err); + err = gcry_cipher_setkey (cfx->hd, dek->key, dek->keylen); + if (err) + return map_gcry_error (err); + if (cfx->mdc) + gcry_md_write (cfx->mdc, temp, nprefix + 2); + gcry_cipher_encrypt (cfx->hd, temp, nprefix + 2, NULL, 0); + gcry_cipher_sync (cfx->hd); + fwrite (temp, 1, nprefix + 2, out); + if (cfx->blkmode.on) + { + cfx->blkmode.nleft -= (nprefix + 2); + if (use_mdc) + cfx->blkmode.nleft--; /* 1 byte version */ + } + return rc; +} + + +static cdk_error_t +write_mdc_packet (FILE * out, cipher_filter_t * cfx) +{ + byte pktdata[22]; + int dlen = gcry_md_get_algo_dlen (GCRY_MD_SHA1); + + if (!out || !cfx) + return CDK_Inv_Value; + if (dlen != 20) + return CDK_Inv_Algo; + + /* We must hash the prefix of the MDC packet here */ + pktdata[0] = 0xD3; + pktdata[1] = 0x14; + gcry_md_write (cfx->mdc, pktdata, 2); + gcry_md_final (cfx->mdc); + memcpy (pktdata + 2, gcry_md_read (cfx->mdc, GCRY_MD_SHA1), dlen); + gcry_cipher_encrypt (cfx->hd, pktdata, dlen + 2, NULL, 0); + fwrite (pktdata, 1, dlen + 2, out); + wipemem (pktdata, sizeof (pktdata)); + return 0; +} + + +static inline int +num2bits (size_t n) +{ + size_t i; + + for (i = 0; n > 1; i++) + n >>= 1; + return i; +} + + +static cdk_error_t +write_partial_block (FILE * in, FILE * out, off_t * r_len, + cipher_filter_t * cfx) +{ + gcry_error_t err; + byte buf[DEF_BLOCKSIZE]; + size_t n; + int nread; + + if (!out || !cfx) + return CDK_Inv_Value; + + if (!cfx->blkmode.nleft && *r_len > 0) + { + if (*r_len > DEF_BLOCKSIZE) + { + /*_cdk_log_debug ("write_partial_block: size %lu block %d\n", + *r_len, DEF_BLOCKSIZE);*/ + fputc ((0xE0 | DEF_BLOCKBITS), out); + cfx->blkmode.nleft = DEF_BLOCKSIZE; + (*r_len) -= DEF_BLOCKSIZE; + } + else if (*r_len > 512) + { + n = num2bits (*r_len); + cfx->blkmode.nleft = (1 << n); + /*_cdk_log_debug ("write_partial_block: size %lu bits %d block %d\n", + *r_len, n, (1<blkmode.nleft; + } + else + { + size_t pktlen = *r_len; + + /* If we use the MDC mode, we need to increase the final + partial body length to hold the mdc packet itself. */ + if (cfx->mdc) + pktlen += 22; + + if (pktlen < 192) + fputc (pktlen, out); + else if (pktlen < 8384) + { + pktlen -= 192; + fputc ((pktlen / 256) + 192, out); + fputc ((pktlen % 256), out); + } + cfx->blkmode.nleft = pktlen; + /*_cdk_log_debug ("write_partial_block: end %d block\n", pktlen);*/ + (*r_len) -= pktlen; + } + } + else + (*r_len) -= cfx->blkmode.nleft; + + n = cfx->blkmode.nleft < DIM (buf) ? cfx->blkmode.nleft : DIM (buf); + nread = fread (buf, 1, n, in); + if (!nread) + return CDK_EOF; + if (cfx->mdc) + gcry_md_write (cfx->mdc, buf, nread); + err = gcry_cipher_encrypt (cfx->hd, buf, nread, NULL, 0); + if (err) + return map_gcry_error (err); + fwrite (buf, 1, nread, out); + cfx->blkmode.nleft -= nread; + return 0; +} + + +static cdk_error_t +cipher_encode_file (void *opaque, FILE * in, FILE * out) +{ + cipher_filter_t *cfx = opaque; + byte buf[BUFSIZE]; + off_t len, len2; + int nread; + cdk_error_t rc; + + if (!cfx || !in || !out) + return CDK_Inv_Value; + + len = len2 = fp_get_length (in); + if (len == (off_t) - 1) + return CDK_File_Error; + while (!feof (in)) + { + if (cfx->blkmode.on) + { + rc = write_partial_block (in, out, &len2, cfx); + if (rc == CDK_EOF) + break; + if (rc) + { + wipemem (buf, sizeof (buf)); + return rc; + } + continue; + } + nread = fread (buf, 1, DIM (buf), in); + if (!nread) + break; + if (cfx->mdc) + gcry_md_write (cfx->mdc, buf, nread); + gcry_cipher_encrypt (cfx->hd, buf, nread, NULL, 0); + fwrite (buf, 1, nread, out); + } + if (cfx->mdc) + rc = write_mdc_packet (out, cfx); + else + rc = 0; + + wipemem (buf, sizeof (buf)); + return rc; +} + + +static cdk_error_t +read_header (cipher_filter_t * cfx, FILE * in) +{ + cdk_dek_t dek; + byte temp[32]; + int blocksize, nprefix; + int i, c; + gcry_error_t err; + + if (!cfx || !in) + return CDK_Inv_Value; + + dek = cfx->dek; + blocksize = gcry_cipher_get_algo_blklen (dek->algo); + if (blocksize < 8 || blocksize > 16) + return CDK_Inv_Algo; + + nprefix = blocksize; + if (cfx->datalen > 0 && cfx->datalen < (nprefix + 2)) + return CDK_Inv_Value; + if (cfx->mdc_method) + { + err = gcry_md_open (&cfx->mdc, cfx->mdc_method, 0); + if (err) + return map_gcry_error (err); + } + err = gcry_cipher_open (&cfx->hd, dek->algo, GCRY_CIPHER_MODE_CFB, + cfx->mdc_method ? 0 : GCRY_CIPHER_ENABLE_SYNC); + if (err) + return map_gcry_error (err); + err = gcry_cipher_setiv (cfx->hd, NULL, 0); + if (err) + return map_gcry_error (err); + err = gcry_cipher_setkey (cfx->hd, dek->key, dek->keylen); + if (err) + return map_gcry_error (err); + + for (i = 0; i < (nprefix + 2); i++) + { + c = fgetc (in); + if (c == EOF) + return CDK_File_Error; + temp[i] = c; + } + gcry_cipher_decrypt (cfx->hd, temp, nprefix + 2, NULL, 0); + gcry_cipher_sync (cfx->hd); + i = nprefix; + if (temp[i - 2] != temp[i] || temp[i - 1] != temp[i + 1]) + return CDK_Chksum_Error; + if (cfx->mdc) + gcry_md_write (cfx->mdc, temp, nprefix + 2); + if (cfx->blkmode.on) + cfx->blkmode.size -= (nprefix + 2); + return 0; +} + + +static cdk_error_t +finalize_mdc (gcry_md_hd_t md, const byte * buf, size_t nread) +{ + byte mdcbuf[20]; + int dlen = gcry_md_get_algo_dlen (GCRY_MD_SHA1); + cdk_error_t rc; + + if (dlen != 20) + return CDK_Inv_Algo; + + if (buf[nread - dlen - 2] == 0xD3 && buf[nread - dlen - 1] == 0x14) + { + gcry_md_write (md, buf, nread - dlen); + gcry_md_final (md); + memcpy (mdcbuf, gcry_md_read (md, GCRY_MD_SHA1), dlen); + if (memcmp (mdcbuf, buf + nread - dlen, dlen)) + rc = CDK_Bad_MDC; + else + rc = CDK_Success; + wipemem (mdcbuf, sizeof (mdcbuf)); + return rc; + } + + return CDK_Inv_Packet; +} + + +static cdk_error_t +cipher_decode_file (void *opaque, FILE * in, FILE * out) +{ + cipher_filter_t *cfx = opaque; + cdk_error_t rc; + byte buf[BUFSIZE]; + int nread, nreq; + + if (!cfx || !in || !out) + return CDK_Inv_Value; + + while (!feof (in)) + { + /*_cdk_log_debug ("partial on=%d size=%lu\n", + cfx->blkmode.on, cfx->blkmode.size);*/ + nreq = cfx->blkmode.on ? cfx->blkmode.size : DIM (buf); + nread = fread (buf, 1, nreq, in); + if (!nread) + break; + gcry_cipher_decrypt (cfx->hd, buf, nread, NULL, 0); + if (feof (in) && cfx->mdc) + { + rc = finalize_mdc (cfx->mdc, buf, nread); + if (rc) + { + wipemem (buf, sizeof (buf)); + return rc; + } + /* We need to correct the size here to avoid the MDC + packet will be written to the output. */ + nread -= 22; + } + else if (cfx->mdc) + gcry_md_write (cfx->mdc, buf, nread); + fwrite (buf, 1, nread, out); + if (cfx->blkmode.on) + { + cfx->blkmode.size = _cdk_pkt_read_len (in, &cfx->blkmode.on); + if (cfx->blkmode.size == (size_t) EOF) + return CDK_Inv_Packet; + } + } + + wipemem (buf, sizeof (buf)); + return 0; +} + + +static cdk_error_t +cipher_decode (void *opaque, FILE * in, FILE * out) +{ + cipher_filter_t *cfx = opaque; + cdk_error_t rc; + + _cdk_log_debug ("cipher filter: decode\n"); + + if (!cfx || !in || !out) + return CDK_Inv_Value; + + rc = read_header (cfx, in); + if (!rc) + rc = cipher_decode_file (cfx, in, out); + return rc; +} + + +static cdk_error_t +cipher_encode (void *opaque, FILE * in, FILE * out) +{ + cipher_filter_t *cfx = opaque; + cdk_error_t rc; + + _cdk_log_debug ("cipher filter: encode\n"); + + if (!cfx || !in || !out) + return CDK_Inv_Value; + + cfx->datalen = fp_get_length (in); + if (cfx->datalen < BUFSIZE && cfx->blkmode.on) + cfx->blkmode.on = 0; + rc = write_header (cfx, out); + if (!rc) + rc = cipher_encode_file (cfx, in, out); + return rc; +} + + +cdk_error_t +_cdk_filter_cipher (void *opaque, int ctl, FILE * in, FILE * out) +{ + if (ctl == STREAMCTL_READ) + return cipher_decode (opaque, in, out); + else if (ctl == STREAMCTL_WRITE) + return cipher_encode (opaque, in, out); + else if (ctl == STREAMCTL_FREE) + { + cipher_filter_t *cfx = opaque; + if (cfx) + { + _cdk_log_debug ("free cipher filter\n"); + gcry_md_close (cfx->mdc); + cfx->mdc = NULL; + gcry_cipher_close (cfx->hd); + cfx->hd = NULL; + return 0; + } + } + return CDK_Inv_Mode; +} diff --git a/src/daemon/https/opencdk/compress.c b/src/daemon/https/opencdk/compress.c new file mode 100644 index 00000000..a984f38d --- /dev/null +++ b/src/daemon/https/opencdk/compress.c @@ -0,0 +1,238 @@ +/* compress.c - Compression filters + * Copyright (C) 2002, 2003 Timo Schulz + * Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation + * + * This file is part of OpenCDK. + * + * OpenCDK 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. + * + * OpenCDK 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. + */ +#ifdef HAVE_CONFIG_H +# include +#endif +#include +#include +#ifdef HAVE_LIBZ +# include +#endif + +#include "opencdk.h" +#include "main.h" +#include "filters.h" + +#ifdef HAVE_LIBZ +static int +compress_data (z_stream * zs, int flush, byte * inbuf, size_t insize, + FILE * out) +{ + int nbytes, zrc; + byte buf[4096]; + + zs->next_in = inbuf; + zs->avail_in = insize; + + do + { + zs->next_out = buf; + zs->avail_out = DIM (buf); + + zrc = deflate (zs, flush); + if (zrc == Z_STREAM_END && flush == Z_FINISH) + ; + else if (zrc != Z_OK) + break; + nbytes = DIM (buf) - zs->avail_out; + fwrite (buf, 1, nbytes, out); + } + while (zs->avail_out == 0 || (flush == Z_FINISH && zrc != Z_STREAM_END)); + return zrc; +} + + +static int +decompress_data (compress_filter_t * zfx, z_stream * zs, + FILE * in, size_t * ret_len) +{ + int nread, nold; + int rc, zrc; + + rc = 0; + nread = 0; + while (zs->avail_out != 0) + { + if (!zs->avail_in) + { + nread = fread (zfx->inbuf, 1, zfx->inbufsize, in); + zs->next_in = zfx->inbuf; + zs->avail_in = nread; + } + nold = zs->avail_out; + zrc = inflate (zs, Z_SYNC_FLUSH); + if (zrc != Z_OK && zrc != Z_STREAM_END) + { + rc = CDK_Zlib_Error; + break; + } + *ret_len = zfx->outbufsize - zs->avail_out; + if (nold == zs->avail_out) + break; + if (zrc == Z_STREAM_END) + { + rc = EOF; /* eof */ + break; + } + } + if (!nread && feof (in)) + rc = -1; + return rc; +} + + +static cdk_error_t +compress_decode (void *opaque, FILE * in, FILE * out) +{ + compress_filter_t *zfx = opaque; + z_stream *zs; + size_t nbytes; + int zrc; + cdk_error_t rc = 0; + + _cdk_log_debug ("compress filter: decode (algo=%d)\n", zfx->algo); + + if (!zfx || !in || !out) + return CDK_Inv_Value; + + zs = cdk_calloc (1, sizeof *zs); + if (!zs) + return CDK_Out_Of_Core; + if (zfx->algo == CDK_COMPRESS_ZIP) + zrc = inflateInit2 (zs, -13); + else + zrc = inflateInit (zs); + if (zrc != Z_OK) + return CDK_Zlib_Error; + + zfx->outbufsize = 8192; + zfx->inbufsize = 2048; + memset (zfx->inbuf, 0, sizeof zfx->inbuf); + zs->avail_in = 0; + + nbytes = 0; + while (rc != -1) + { + zs->next_out = zfx->outbuf; + zs->avail_out = 8192; + rc = decompress_data (zfx, zs, in, &nbytes); + fwrite (zfx->outbuf, 1, nbytes, out); + } + inflateEnd (zs); + cdk_free (zs); + if (rc == CDK_EOF) + rc = 0; + return rc; +} + + +static cdk_error_t +compress_encode (void *opaque, FILE * in, FILE * out) +{ + compress_filter_t *zfx = opaque; + z_stream *zs; + struct cdk_pkt_compressed_s cd; + struct cdk_packet_s pkt; + int zrc, nread; + cdk_error_t rc; + + _cdk_log_debug ("compress filter: encode\n"); + + if (!zfx || !in || !out) + return CDK_Inv_Value; + + if (!zfx->algo) + zfx->algo = CDK_COMPRESS_ZIP; + + memset (&cd, 0, sizeof (cd)); + cd.len = 0; + cd.algorithm = zfx->algo; + pkt.pkttype = CDK_PKT_COMPRESSED; + pkt.pkt.compressed = &cd; + rc = _cdk_pkt_write_fp (out, &pkt); + if (rc) + return rc; + + zs = cdk_calloc (1, sizeof *zs); + if (!zs) + return CDK_Out_Of_Core; + if (zfx->algo == CDK_COMPRESS_ZIP) + rc = deflateInit2 (zs, zfx->level, Z_DEFLATED, -13, 8, + Z_DEFAULT_STRATEGY); + else + rc = deflateInit (zs, zfx->level); + if (rc != Z_OK) + { + cdk_free (zs); + return CDK_Zlib_Error; + } + zfx->outbufsize = 8192; + memset (zfx->outbuf, 0, sizeof zfx->outbuf); + + while (!feof (in)) + { + nread = fread (zfx->outbuf, 1, zfx->outbufsize, in); + if (!nread) + break; + zrc = compress_data (zs, Z_NO_FLUSH, zfx->outbuf, nread, out); + if (zrc) + { + rc = CDK_Zlib_Error; + break; + } + } + if (!rc) + { + nread = 0; + zrc = compress_data (zs, Z_FINISH, zfx->outbuf, nread, out); + if (zrc != Z_STREAM_END) + rc = CDK_Zlib_Error; + } + deflateEnd (zs); + cdk_free (zs); + return rc; +} + + +cdk_error_t +_cdk_filter_compress (void *opaque, int ctl, FILE * in, FILE * out) +{ + if (ctl == STREAMCTL_READ) + return compress_decode (opaque, in, out); + else if (ctl == STREAMCTL_WRITE) + return compress_encode (opaque, in, out); + else if (ctl == STREAMCTL_FREE) + { + compress_filter_t *zfx = opaque; + if (zfx) + { + _cdk_log_debug ("free compress filter\n"); + zfx->level = 0; + zfx->algo = 0; + return 0; + } + } + return CDK_Inv_Mode; +} + +#else +cdk_error_t +_cdk_filter_compress (void *opaque, int ctl, FILE * in, FILE * out) +{ + return CDK_Not_Implemented; +} +#endif /* HAVE_LIBZ */ diff --git a/src/daemon/https/opencdk/context.h b/src/daemon/https/opencdk/context.h new file mode 100644 index 00000000..e6569523 --- /dev/null +++ b/src/daemon/https/opencdk/context.h @@ -0,0 +1,120 @@ +/* context.h + * Copyright (C) 2002, 2003, 2007 Timo Schulz + * + * This file is part of OpenCDK. + * + * OpenCDK 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. + * + * OpenCDK 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. + */ +#ifndef CDK_CONTEXT_H +#define CDK_CONTEXT_H + +#include "types.h" + +struct cdk_listkey_s { + unsigned init:1; + cdk_stream_t inp; + cdk_keydb_hd_t db; + int type; + union { + char *patt; + cdk_strlist_t fpatt; + } u; + cdk_strlist_t t; +}; + + +struct cdk_s2k_s { + int mode; + byte hash_algo; + byte salt[8]; + u32 count; +}; + + +struct cdk_ctx_s { + int cipher_algo; + int digest_algo; + struct { + int algo; + int level; + } compress; + struct { + int mode; + int digest_algo; + } _s2k; + struct { + unsigned blockmode:1; + unsigned armor:1; + unsigned textmode:1; + unsigned compress:1; + unsigned mdc:1; + unsigned overwrite; + unsigned force_digest:1; + } opt; + struct { + cdk_verify_result_t verify; + } result; + struct { + cdk_pkt_seckey_t sk; + unsigned on:1; + } cache; + cdk_dek_t dek; + struct { + cdk_keydb_hd_t sec; + cdk_keydb_hd_t pub; + unsigned int close_db:1; + } db; + char *(*passphrase_cb) (void *opaque, const char *prompt); + void * passphrase_cb_value; +}; + +struct cdk_prefitem_s { + byte type; + byte value; +}; + +struct cdk_desig_revoker_s { + struct cdk_desig_revoker_s * next; + byte r_class; + byte algid; + byte fpr[KEY_FPR_LEN]; +}; + +struct cdk_subpkt_s { + struct cdk_subpkt_s * next; + u32 size; + byte type; + byte d[1]; +}; + +struct cdk_keylist_s { + struct cdk_keylist_s * next; + union { + cdk_pkt_pubkey_t pk; + cdk_pkt_seckey_t sk; + } key; + int version; + int type; +}; + +struct cdk_dek_s { + int algo; + int keylen; + int use_mdc; + byte key[32]; /* 256-bit */ +}; + +struct cdk_strlist_s { + struct cdk_strlist_s * next; + char d[1]; +}; + +#endif /* CDK_CONTEXT_H */ diff --git a/src/daemon/https/opencdk/dummy.c b/src/daemon/https/opencdk/dummy.c new file mode 100644 index 00000000..2132f2d9 --- /dev/null +++ b/src/daemon/https/opencdk/dummy.c @@ -0,0 +1,15 @@ +#include +#include + +#include "opencdk.h" +#include "main.h" +#include "filters.h" +#include "packet.h" + +cdk_error_t +_cdk_proc_packets (cdk_ctx_t hd, cdk_stream_t inp, cdk_stream_t data, + const char *output, cdk_stream_t outstream, + gcry_md_hd_t md) +{ + return 0; +} diff --git a/src/daemon/https/opencdk/filters.h b/src/daemon/https/opencdk/filters.h new file mode 100644 index 00000000..a60881ed --- /dev/null +++ b/src/daemon/https/opencdk/filters.h @@ -0,0 +1,95 @@ +/* filters.h - Filter structs + * Copyright (C) 2002, 2003 Timo Schulz + * + * This file is part of OpenCDK. + * + * OpenCDK 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. + * + * OpenCDK 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. + */ +#ifndef CDK_FILTERS_H +#define CDK_FILTERS_H + +enum { + STREAMCTL_READ = 0, + STREAMCTL_WRITE = 1, + STREAMCTL_FREE = 2 +}; + +typedef struct { + gcry_cipher_hd_t hd; + gcry_md_hd_t mdc; + int mdc_method; + cdk_dek_t dek; + u32 datalen; + struct { + size_t on; + off_t size; + off_t nleft; + } blkmode; + cdk_stream_t s; +} cipher_filter_t; + +typedef struct { + int digest_algo; + gcry_md_hd_t md; +} md_filter_t; + +typedef struct { + const char *le; /* line endings */ + const char *hdrlines; + u32 crc; + int crc_okay; + int idx, idx2; +} armor_filter_t; + +typedef struct { + cdk_lit_format_t mode; + char *orig_filename; /* This original name of the input file. */ + char *filename; + gcry_md_hd_t md; + struct { + size_t on; + off_t size; + } blkmode; +} literal_filter_t; + +typedef struct { + size_t inbufsize; + byte inbuf[8192]; + size_t outbufsize; + byte outbuf[8192]; + int algo; /* compress algo */ + int level; +} compress_filter_t; + +typedef struct { + const char * lf; +} text_filter_t; + + +/*-- armor.c -*/ +int _cdk_filter_armor( void * opaque, int ctl, FILE * in, FILE * out ); + +/*-- cipher.c --*/ +cdk_error_t _cdk_filter_hash( void * opaque, int ctl, FILE * in, FILE * out ); +cdk_error_t _cdk_filter_cipher( void * opaque, int ctl, + FILE * in, FILE * out ); + +/*-- literal.c --*/ +int _cdk_filter_literal( void * opaque, int ctl, FILE * in, FILE * out ); +int _cdk_filter_text( void * opaque, int ctl, FILE * in, FILE * out ); + +/*-- compress.c --*/ +cdk_error_t _cdk_filter_compress( void * opaque, int ctl, + FILE * in, FILE * out ); + +#endif /* CDK_FILTERS_H */ + + diff --git a/src/daemon/https/opencdk/kbnode.c b/src/daemon/https/opencdk/kbnode.c new file mode 100644 index 00000000..9028c24b --- /dev/null +++ b/src/daemon/https/opencdk/kbnode.c @@ -0,0 +1,581 @@ +/* kbnode.c - keyblock node utility functions + * Copyright (C) 1998-2001 Free Software Foundation, Inc. + * Copyright (C) 2002, 2003, 2007 Timo Schulz + * + * This file is part of OpenCDK. + * + * OpenCDK 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. + * + * OpenCDK 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. + */ +#ifdef HAVE_CONFIG_H +# include +#endif +#include +#include +#include +#include + +#include "opencdk.h" +#include "main.h" +#include "packet.h" + + +/** + * cdk_kbnode_new: + * @pkt: the packet to add + * + * Allocate a new key node and add the packet. + **/ +cdk_kbnode_t +cdk_kbnode_new (cdk_packet_t pkt) +{ + cdk_kbnode_t n; + + n = cdk_calloc (1, sizeof *n); + if (!n) + return NULL; + n->pkt = pkt; + return n; +} + + +void +_cdk_kbnode_clone (cdk_kbnode_t node) +{ + /* Mark the node as clone which means that the packet + will not be freed, just the node itself. */ + if (node) + node->is_cloned = 1; +} + + +/** + * cdk_kbnode_release: + * @n: the key node + * + * Release the memory of the node. + **/ +void +cdk_kbnode_release (cdk_kbnode_t node) +{ + cdk_kbnode_t n2; + + while (node) + { + n2 = node->next; + if (!node->is_cloned) + cdk_pkt_release (node->pkt); + cdk_free (node); + node = n2; + } +} + + +/** + * cdk_kbnode_delete: + * @node: the key node + * + * Mark the given node as deleted. + **/ +void +cdk_kbnode_delete (cdk_kbnode_t node) +{ + if (node) + node->is_deleted = 1; +} + + +/* Append NODE to ROOT. ROOT must exist! */ +void +_cdk_kbnode_add (cdk_kbnode_t root, cdk_kbnode_t node) +{ + cdk_kbnode_t n1; + + for (n1 = root; n1->next; n1 = n1->next) + ; + n1->next = node; +} + + +/** + * cdk_kbnode_insert: + * @root: the root key node + * @node: the node to add + * @pkttype: packet type + * + * Insert @node into the list after @root but before a packet which is not of + * type @pkttype (only if @pkttype != 0). + **/ +void +cdk_kbnode_insert (cdk_kbnode_t root, cdk_kbnode_t node, int pkttype) +{ + if (!pkttype) + { + node->next = root->next; + root->next = node; + } + else + { + cdk_kbnode_t n1; + + for (n1 = root; n1->next; n1 = n1->next) + if (pkttype != n1->next->pkt->pkttype) + { + node->next = n1->next; + n1->next = node; + return; + } + /* No such packet, append */ + node->next = NULL; + n1->next = node; + } +} + + +/** + * cdk_kbnode_find_prev: + * @root: the root key node + * @node: the key node + * @pkttype: packet type + * + * Find the previous node (if @pkttype = 0) or the previous node + * with pkttype @pkttype in the list starting with @root of @node. + **/ +cdk_kbnode_t +cdk_kbnode_find_prev (cdk_kbnode_t root, cdk_kbnode_t node, int pkttype) +{ + cdk_kbnode_t n1; + + for (n1 = NULL; root && root != node; root = root->next) + { + if (!pkttype || root->pkt->pkttype == pkttype) + n1 = root; + } + return n1; +} + + +/** + * cdk_kbnode_find_next: + * @node: the key node + * @pkttype: packet type + * + * Ditto, but find the next packet. The behaviour is trivial if + * @pkttype is 0 but if it is specified, the next node with a packet + * of this type is returned. The function has some knowledge about + * the valid ordering of packets: e.g. if the next signature packet + * is requested, the function will not return one if it encounters + * a user-id. + **/ +cdk_kbnode_t +cdk_kbnode_find_next (cdk_kbnode_t node, int pkttype) +{ + for (node = node->next; node; node = node->next) + { + if (!pkttype) + return node; + else if (pkttype == CDK_PKT_USER_ID && + (node->pkt->pkttype == CDK_PKT_PUBLIC_KEY || + node->pkt->pkttype == CDK_PKT_SECRET_KEY)) + return NULL; + else if (pkttype == CDK_PKT_SIGNATURE && + (node->pkt->pkttype == CDK_PKT_USER_ID || + node->pkt->pkttype == CDK_PKT_PUBLIC_KEY || + node->pkt->pkttype == CDK_PKT_SECRET_KEY)) + return NULL; + else if (node->pkt->pkttype == pkttype) + return node; + } + return NULL; +} + + +/** + * cdk_kbnode_find: + * @node: the key node + * @pkttype: packet type + * + * Try to find the next node with the packettype @pkttype. + **/ +cdk_kbnode_t +cdk_kbnode_find (cdk_kbnode_t node, int pkttype) +{ + for (; node; node = node->next) + { + if (node->pkt->pkttype == pkttype) + return node; + } + return NULL; +} + + +/** + * cdk_kbnode_find_packet: + * @node: the key node + * @pkttype: packet type + * + * Same as cdk_kbnode_find but it returns the packet instead of the node. + **/ +cdk_packet_t +cdk_kbnode_find_packet (cdk_kbnode_t node, int pkttype) +{ + cdk_kbnode_t res; + + res = cdk_kbnode_find (node, pkttype); + return res ? res->pkt : NULL; +} + + +/**************** + * Walk through a list of kbnodes. This function returns + * the next kbnode for each call; before using the function the first + * time, the caller must set CONTEXT to NULL (This has simply the effect + * to start with ROOT). + */ +cdk_kbnode_t +cdk_kbnode_walk (cdk_kbnode_t root, cdk_kbnode_t * context, int all) +{ + cdk_kbnode_t n; + + do + { + if (!*context) + { + *context = root; + n = root; + } + else + { + n = (*context)->next; + *context = n; + } + } + while (!all && n && n->is_deleted); + return n; +} + + +/** + * cdk_kbnode_commit: + * @root: the nodes + * + * Commit changes made to the kblist at ROOT. Note that ROOT my change, + * and it is therefore passed by reference. + * The function has the effect of removing all nodes marked as deleted. + * returns true if any node has been changed + */ +int +cdk_kbnode_commit (cdk_kbnode_t * root) +{ + cdk_kbnode_t n, nl; + int changed = 0; + + for (n = *root, nl = NULL; n; n = nl->next) + { + if (n->is_deleted) + { + if (n == *root) + *root = nl = n->next; + else + nl->next = n->next; + if (!n->is_cloned) + cdk_pkt_release (n->pkt); + cdk_free (n); + changed = 1; + } + else + nl = n; + } + return changed; +} + + +/** + * cdk_kbnode_remove: + * @root: the root node + * @node: the node to delete + * + * Remove a node from the root node. + */ +void +cdk_kbnode_remove (cdk_kbnode_t * root, cdk_kbnode_t node) +{ + cdk_kbnode_t n, nl; + + for (n = *root, nl = NULL; n; n = nl->next) + { + if (n == node) + { + if (n == *root) + *root = nl = n->next; + else + nl->next = n->next; + if (!n->is_cloned) + cdk_pkt_release (n->pkt); + cdk_free (n); + } + else + nl = n; + } +} + + + +/** + * cdk_cdknode_move: + * @root: root node + * @node: the node to move + * @where: destination place where to move the node. + * + * Move NODE behind right after WHERE or to the beginning if WHERE is NULL. + */ +void +cdk_kbnode_move (cdk_kbnode_t * root, cdk_kbnode_t node, cdk_kbnode_t where) +{ + cdk_kbnode_t tmp, prev; + + if (!root || !*root || !node) + return; + for (prev = *root; prev && prev->next != node; prev = prev->next) + ; + if (!prev) + return; /* Node is not in the list */ + + if (!where) + { /* Move node before root */ + if (node == *root) + return; + prev->next = node->next; + node->next = *root; + *root = node; + return; + } + if (node == where) /* Move it after where. */ + return; + tmp = node->next; + node->next = where->next; + where->next = node; + prev->next = tmp; +} + + +/** + * cdk_kbnode_get_packet: + * @node: the key node + * + * Return the packet which is stored inside the node in @node. + **/ +cdk_packet_t +cdk_kbnode_get_packet (cdk_kbnode_t node) +{ + if (node) + return node->pkt; + return NULL; +} + + +/** + * cdk_kbnode_read_from_mem: + * @ret_node: the new key node + * @buf: the buffer which stores the key sequence + * @buflen: the length of the buffer + * + * Try to read a key node from the memory buffer @buf. + **/ +cdk_error_t +cdk_kbnode_read_from_mem (cdk_kbnode_t * ret_node, + const byte * buf, size_t buflen) +{ + cdk_stream_t inp; + cdk_error_t rc; + + if (!buflen || !ret_node || !buf) + return CDK_Inv_Value; + + *ret_node = NULL; + rc = cdk_stream_tmp_from_mem (buf, buflen, &inp); + if (rc) + return rc; + rc = cdk_keydb_get_keyblock (inp, ret_node); + cdk_stream_close (inp); + return rc; +} + +/** + * cdk_kbnode_write_to_mem_alloc: + * @node: the key node + * @r_buf: buffer to hold the raw data + * @r_buflen: buffer length of the allocated raw data. + * + * The function acts similar to cdk_kbnode_write_to_mem but + * it allocates the buffer to avoid the lengthy second run. + */ +cdk_error_t +cdk_kbnode_write_to_mem_alloc (cdk_kbnode_t node, + byte ** r_buf, size_t * r_buflen) +{ + cdk_kbnode_t n; + cdk_stream_t s; + cdk_error_t rc; + size_t len; + + if (!node) + return CDK_Inv_Value; + + *r_buf = NULL; + *r_buflen = 0; + + rc = cdk_stream_tmp_new (&s); + if (rc) + return rc; + + for (n = node; n; n = n->next) + { + /* Skip all packets which cannot occur in a key composition. */ + if (n->pkt->pkttype != CDK_PKT_PUBLIC_KEY && + n->pkt->pkttype != CDK_PKT_PUBLIC_SUBKEY && + n->pkt->pkttype != CDK_PKT_SECRET_KEY && + n->pkt->pkttype != CDK_PKT_SECRET_SUBKEY && + n->pkt->pkttype != CDK_PKT_SIGNATURE && + n->pkt->pkttype != CDK_PKT_USER_ID && + n->pkt->pkttype != CDK_PKT_ATTRIBUTE) + continue; + rc = cdk_pkt_write (s, n->pkt); + if (rc) + { + cdk_stream_close (s); + return rc; + } + } + + cdk_stream_seek (s, 0); + len = cdk_stream_get_length (s); + *r_buf = cdk_calloc (1, len); + *r_buflen = cdk_stream_read (s, *r_buf, len); + cdk_stream_close (s); + return 0; +} + + +/** + * cdk_kbnode_write_to_mem: + * @node: the key node + * @buf: the buffer to store the node data + * @r_nbytes: the new length of the buffer. + * + * Try to write the contents of the key node to the buffer @buf and + * return the length of it in @r_nbytes. If buf is zero, only the + * length of the node is calculated and returned in @r_nbytes. + * Whenever it is possible, the cdk_kbnode_write_to_mem_alloc should be used. + **/ +cdk_error_t +cdk_kbnode_write_to_mem (cdk_kbnode_t node, byte * buf, size_t * r_nbytes) +{ + cdk_kbnode_t n; + cdk_stream_t s; + size_t len; + cdk_error_t rc; + + if (!node) + return CDK_Inv_Value; + + rc = cdk_stream_tmp_new (&s); + if (rc) + return rc; + + for (n = node; n; n = n->next) + { + /* Skip all packets which cannot occur in a key composition. */ + if (n->pkt->pkttype != CDK_PKT_PUBLIC_KEY && + n->pkt->pkttype != CDK_PKT_PUBLIC_SUBKEY && + n->pkt->pkttype != CDK_PKT_SECRET_KEY && + n->pkt->pkttype != CDK_PKT_SECRET_SUBKEY && + n->pkt->pkttype != CDK_PKT_SIGNATURE && + n->pkt->pkttype != CDK_PKT_USER_ID && + n->pkt->pkttype != CDK_PKT_ATTRIBUTE) + continue; + rc = cdk_pkt_write (s, n->pkt); + if (rc) + { + cdk_stream_close (s); + return rc; + } + } + + cdk_stream_seek (s, 0); + len = cdk_stream_get_length (s); + if (!buf) + { + *r_nbytes = len; /* Only return the length of the buffer */ + cdk_stream_close (s); + return 0; + } + if (*r_nbytes < len) + rc = CDK_Too_Short; + if (!rc) + *r_nbytes = cdk_stream_read (s, buf, len); + cdk_stream_close (s); + return rc; +} + + +/** + * cdk_kbnode_hash: + * @node: the key node + * @hashctx: opaque pointer to the hash context + * @is_v4: OpenPGP signature (yes=1, no=0) + * @pkttype: packet type to hash (if zero use the packet type from the node) + * @flags: flags which depend on the operation + * + * Hash the key node contents. Two modes are supported. If the packet + * type is used (!= 0) then the function searches the first node with + * this type. Otherwise the node is seen as a single node and the type + * is extracted from it. + **/ +cdk_error_t +cdk_kbnode_hash (cdk_kbnode_t node, gcry_md_hd_t md, int is_v4, + int pkttype, int flags) +{ + cdk_packet_t pkt; + + if (!node || !md) + return CDK_Inv_Value; + if (!pkttype) + { + pkt = cdk_kbnode_get_packet (node); + pkttype = pkt->pkttype; + } + else + { + pkt = cdk_kbnode_find_packet (node, pkttype); + if (!pkt) + return CDK_Inv_Packet; + } + + switch (pkttype) + { + case CDK_PKT_PUBLIC_KEY: + case CDK_PKT_PUBLIC_SUBKEY: + _cdk_hash_pubkey (pkt->pkt.public_key, md, flags & 1); + break; + + case CDK_PKT_USER_ID: + _cdk_hash_userid (pkt->pkt.user_id, is_v4, md); + break; + + case CDK_PKT_SIGNATURE: + _cdk_hash_sig_data (pkt->pkt.signature, md); + break; + + default: + return CDK_Inv_Mode; + } + return 0; +} diff --git a/src/daemon/https/opencdk/keydb.c b/src/daemon/https/opencdk/keydb.c new file mode 100644 index 00000000..0553d3c3 --- /dev/null +++ b/src/daemon/https/opencdk/keydb.c @@ -0,0 +1,2303 @@ +/* keydb.c - Key database routines + * Copyright (C) 2002, 2003, 2007 Timo Schulz + * + * This file is part of OpenCDK. + * + * OpenCDK 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. + * + * OpenCDK 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. + */ +#ifdef HAVE_CONFIG_H +# include +#endif +#include +#include +#include +#include +#include +#include + +#include "opencdk.h" +#include "main.h" +#include "packet.h" +#include "filters.h" +#include "stream.h" + + +#define KEYID_CMP(a, b) ((a[0]) == (b[0]) && (a[1]) == (b[1])) +#define KEYDB_CACHE_ENTRIES 8 + + +/* Internal key index structure. */ +struct key_idx_s +{ + off_t offset; + u32 keyid[2]; + byte fpr[KEY_FPR_LEN]; +}; +typedef struct key_idx_s *key_idx_t; + + +/* Internal handle for the search operation. */ +struct cdk_dbsearch_s +{ + union + { + char *pattern; /* A search is performed by pattern. */ + u32 keyid[2]; /* A search by keyid. */ + byte fpr[KEY_FPR_LEN]; /* A search by fingerprint. */ + } u; + int type; +}; +typedef struct cdk_dbsearch_s *cdk_dbsearch_t; + +/* Internal key cache to associate a key with an file offset. */ +struct key_table_s +{ + struct key_table_s *next; + off_t offset; + cdk_dbsearch_t desc; +}; +typedef struct key_table_s *key_table_t; + +/* Internal key database handle. */ +struct cdk_keydb_hd_s +{ + int type; /* type of the key db handle. */ + int fp_ref; /* 1=means it is a reference and shall not be closed. */ + cdk_stream_t fp; + cdk_stream_t idx; + cdk_dbsearch_t dbs; + char *name; /* name of the underlying file or NULL. */ + char *idx_name; /* name of the index file or NULL. */ + struct key_table_s *cache; + size_t ncache; + unsigned int secret:1; /* contain secret keys. */ + unsigned int isopen:1; /* the underlying stream is opened. */ + unsigned int no_cache:1; /* disable the index cache. */ + unsigned int search:1; /* handle is in search mode. */ + + /* structure to store some stats about the keydb. */ + struct + { + size_t new_keys; /* amount of new keys that were imported. */ + } stats; +}; + + +static void keydb_cache_free (key_table_t cache); +static int keydb_search_copy (cdk_dbsearch_t * r_dst, cdk_dbsearch_t src); +static void keydb_search_free (cdk_dbsearch_t dbs); +static int classify_data (const byte * buf, size_t len); +static cdk_kbnode_t find_selfsig_node (cdk_kbnode_t key, cdk_pkt_pubkey_t pk); + + +static char * +keydb_idx_mkname (const char *file) +{ + char *fname, *fmt; + + fmt = "%s.idx"; + fname = cdk_calloc (1, strlen (file) + strlen (fmt) + 1); + if (!fname) + return NULL; + sprintf (fname, fmt, file); + return fname; +} + + +/* This functions builds an index of the keyring into a separate file + with the name keyring.ext.idx. It contains the offset of all public- + and public subkeys. The format of the file is: + -------- + 4 octets offset of the packet + 8 octets keyid + 20 octets fingerprint + -------- + We store the keyid and the fingerprint due to the fact we can't get + the keyid from a v3 fingerprint directly. +*/ +static cdk_error_t +keydb_idx_build (const char *file) +{ + cdk_packet_t pkt; + cdk_stream_t inp, out = NULL; + byte buf[4 + 8 + KEY_FPR_LEN]; + char *idx_name; + u32 keyid[2]; + cdk_error_t rc; + + if (!file) + return CDK_Inv_Value; + + rc = cdk_stream_open (file, &inp); + if (rc) + return rc; + + idx_name = keydb_idx_mkname (file); + if (!idx_name) + { + cdk_stream_close (inp); + return CDK_Out_Of_Core; + } + rc = cdk_stream_create (idx_name, &out); + cdk_free (idx_name); + if (rc) + { + cdk_stream_close (inp); + return rc; + } + + cdk_pkt_new (&pkt); + while (!cdk_stream_eof (inp)) + { + off_t pos = cdk_stream_tell (inp); + + rc = cdk_pkt_read (inp, pkt); + if (rc) + { + _cdk_log_debug ("index build failed packet off=%lu\n", pos); + /* FIXME: The index is incomplete */ + break; + } + if (pkt->pkttype == CDK_PKT_PUBLIC_KEY || + pkt->pkttype == CDK_PKT_PUBLIC_SUBKEY) + { + _cdk_u32tobuf (pos, buf); + cdk_pk_get_keyid (pkt->pkt.public_key, keyid); + _cdk_u32tobuf (keyid[0], buf + 4); + _cdk_u32tobuf (keyid[1], buf + 8); + cdk_pk_get_fingerprint (pkt->pkt.public_key, buf + 12); + cdk_stream_write (out, buf, 4 + 8 + KEY_FPR_LEN); + } + cdk_pkt_free (pkt); + } + + cdk_pkt_release (pkt); + + cdk_stream_close (out); + cdk_stream_close (inp); + return rc; +} + + +/** + * cdk_keydb_idx_rebuild: + * @hd: key database handle + * + * Rebuild the key index files for the given key database. + **/ +cdk_error_t +cdk_keydb_idx_rebuild (cdk_keydb_hd_t db) +{ + struct stat stbuf; + char *tmp_idx_name; + cdk_error_t rc; + int err; + + if (!db || !db->name) + return CDK_Inv_Value; + if (db->secret) + return 0; + + tmp_idx_name = keydb_idx_mkname (db->name); + if (!tmp_idx_name) + return CDK_Out_Of_Core; + err = stat (tmp_idx_name, &stbuf); + cdk_free (tmp_idx_name); + /* This function expects an existing index which can be rebuild, + if no index exists we do not build one and just return. */ + if (err) + return 0; + + cdk_stream_close (db->idx); + db->idx = NULL; + if (!db->idx_name) + { + db->idx_name = keydb_idx_mkname (db->name); + if (!db->idx_name) + return CDK_Out_Of_Core; + } + rc = keydb_idx_build (db->name); + if (!rc) + rc = cdk_stream_open (db->idx_name, &db->idx); + return rc; +} + + +static cdk_error_t +keydb_idx_parse (cdk_stream_t inp, key_idx_t * r_idx) +{ + key_idx_t idx; + byte buf[4]; + + if (!inp || !r_idx) + return CDK_Inv_Value; + + idx = cdk_calloc (1, sizeof *idx); + if (!idx) + return CDK_Out_Of_Core; + + while (!cdk_stream_eof (inp)) + { + if (cdk_stream_read (inp, buf, 4) == CDK_EOF) + break; + idx->offset = _cdk_buftou32 (buf); + cdk_stream_read (inp, buf, 4); + idx->keyid[0] = _cdk_buftou32 (buf); + cdk_stream_read (inp, buf, 4); + idx->keyid[1] = _cdk_buftou32 (buf); + cdk_stream_read (inp, idx->fpr, KEY_FPR_LEN); + break; + } + *r_idx = idx; + return cdk_stream_eof (inp) ? CDK_EOF : 0; +} + + +static cdk_error_t +keydb_idx_search (cdk_stream_t inp, u32 * keyid, const byte * fpr, + off_t * r_off) +{ + key_idx_t idx; + + if (!inp || !r_off) + return CDK_Inv_Value; + if ((keyid && fpr) || (!keyid && !fpr)) + return CDK_Inv_Mode; + + /* We need an initialize the offset var with a value + because it might be possible the returned offset will + be 0 and then we cannot differ between the begin and an EOF. */ + *r_off = 0xFFFFFFFF; + cdk_stream_seek (inp, 0); + while (keydb_idx_parse (inp, &idx) != CDK_EOF) + { + if (keyid && KEYID_CMP (keyid, idx->keyid)) + { + *r_off = idx->offset; + break; + } + else if (fpr && !memcmp (idx->fpr, fpr, KEY_FPR_LEN)) + { + *r_off = idx->offset; + break; + } + cdk_free (idx); + idx = NULL; + } + cdk_free (idx); + return *r_off != 0xFFFFFFFF ? 0 : CDK_EOF; +} + + +/** + * cdk_keydb_new_from_mem: + * @r_hd: The keydb output handle. + * @data: The raw key data. + * @datlen: The length of the raw data. + * + * Create a new keyring db handle from the contents of a buffer. + */ +cdk_error_t +cdk_keydb_new_from_mem (cdk_keydb_hd_t * r_db, int secret, + const void *data, size_t datlen) +{ + cdk_keydb_hd_t db; + cdk_error_t rc; + + if (!r_db) + return CDK_Inv_Value; + *r_db = NULL; + db = calloc (1, sizeof *db); + rc = cdk_stream_tmp_from_mem (data, datlen, &db->fp); + if (!db->fp) + { + cdk_free (db); + return rc; + } + if (cdk_armor_filter_use (db->fp)) + cdk_stream_set_armor_flag (db->fp, 0); + db->type = CDK_DBTYPE_DATA; + db->secret = secret; + *r_db = db; + return 0; +} + + +/** + * cdk_keydb_new_from_stream: + * @r_hd: the output keydb handle + * @secret: does the stream contain secret key data + * @in: the input stream to use + * + * This function creates a new keydb handle based on the given + * stream. The stream is not closed in cdk_keydb_free() and it + * is up to the caller to close it. No decoding is done. + */ +cdk_error_t +cdk_keydb_new_from_stream (cdk_keydb_hd_t * r_hd, int secret, cdk_stream_t in) +{ + cdk_keydb_hd_t hd; + + if (!r_hd) + return CDK_Inv_Value; + *r_hd = NULL; + + hd = calloc (1, sizeof *hd); + hd->fp = in; + hd->fp_ref = 1; + hd->type = CDK_DBTYPE_STREAM; + hd->secret = secret; + *r_hd = hd; + + /* We do not push any filters and thus we expect that the format + of the stream has the format the user wanted. */ + + return 0; +} + + +cdk_error_t +cdk_keydb_new_from_file (cdk_keydb_hd_t * r_hd, int secret, const char *fname) +{ + cdk_keydb_hd_t hd; + + if (!r_hd) + return CDK_Inv_Value; + *r_hd = NULL; + hd = calloc (1, sizeof *hd); + hd->name = cdk_strdup (fname); + if (!hd->name) + { + cdk_free (hd); + return CDK_Out_Of_Core; + } + hd->type = secret ? CDK_DBTYPE_SK_KEYRING : CDK_DBTYPE_PK_KEYRING; + hd->secret = secret; + *r_hd = hd; + return 0; +} + + + +/** + * cdk_keydb_new: + * @r_hd: handle to store the new keydb object + * @type: type of the keyring + * @data: data which depends on the keyring type + * @count: length of the data + * + * Create a new keydb object + **/ +cdk_error_t +cdk_keydb_new (cdk_keydb_hd_t * r_hd, int type, void *data, size_t count) +{ + switch (type) + { + case CDK_DBTYPE_PK_KEYRING: + case CDK_DBTYPE_SK_KEYRING: + return cdk_keydb_new_from_file (r_hd, type == CDK_DBTYPE_SK_KEYRING, + (const char *) data); + + case CDK_DBTYPE_DATA: + return cdk_keydb_new_from_mem (r_hd, 0, data, count); + + case CDK_DBTYPE_STREAM: + return cdk_keydb_new_from_stream (r_hd, 0, (cdk_stream_t) data); + + default: + return CDK_Inv_Mode; + } + return CDK_Inv_Mode; +} + + +/** + * cdk_keydb_free: + * @hd: the keydb object + * + * Free the keydb object. + **/ +void +cdk_keydb_free (cdk_keydb_hd_t hd) +{ + if (!hd) + return; + + if (hd->name) + { + cdk_free (hd->name); + hd->name = NULL; + } + + if (hd->fp && !hd->fp_ref) + { + cdk_stream_close (hd->fp); + hd->fp = NULL; + } + + if (hd->idx) + { + cdk_stream_close (hd->idx); + hd->idx = NULL; + } + + hd->isopen = 0; + hd->no_cache = 0; + hd->secret = 0; + keydb_cache_free (hd->cache); + hd->cache = NULL; + keydb_search_free (hd->dbs); + hd->dbs = NULL; + cdk_free (hd); +} + + +cdk_error_t +_cdk_keydb_open (cdk_keydb_hd_t hd, cdk_stream_t * ret_kr) +{ + cdk_error_t rc, ec; + + if (!hd || !ret_kr) + return CDK_Inv_Value; + + rc = 0; + if ((hd->type == CDK_DBTYPE_DATA || hd->type == CDK_DBTYPE_STREAM) + && hd->fp) + cdk_stream_seek (hd->fp, 0); + else if (hd->type == CDK_DBTYPE_PK_KEYRING || + hd->type == CDK_DBTYPE_SK_KEYRING) + { + if (!hd->isopen && hd->name) + { + rc = cdk_stream_open (hd->name, &hd->fp); + if (rc) + goto leave; + if (cdk_armor_filter_use (hd->fp)) + cdk_stream_set_armor_flag (hd->fp, 0); + hd->isopen = 1; + /* We disable the index cache for smaller keyrings. */ + if (cdk_stream_get_length (hd->fp) < 524288) + { + hd->no_cache = 1; + goto leave; + } + cdk_free (hd->idx_name); + hd->idx_name = keydb_idx_mkname (hd->name); + if (!hd->idx_name) + { + rc = CDK_Out_Of_Core; + goto leave; + } + ec = cdk_stream_open (hd->idx_name, &hd->idx); + if (ec && !hd->secret) + { + rc = keydb_idx_build (hd->name); + if (!rc) + rc = cdk_stream_open (hd->idx_name, &hd->idx); + if (!rc) + _cdk_log_debug ("create key index table\n"); + else + { + /* This is no real error, it just means we can't create + the index at the given directory. maybe we've no write + access. in this case, we simply disable the index. */ + _cdk_log_debug ("disable key index table err=%d\n", rc); + rc = 0; + hd->no_cache = 1; + } + } + } + else + { + /* We use the cache to search keys, so we always rewind the + STREAM. Except when the _NEXT search mode is used because + this mode is an enumeration and no seeking is needed. */ + if (!hd->search || + (hd->search && hd->dbs->type != CDK_DBSEARCH_NEXT)) + cdk_stream_seek (hd->fp, 0); + } + } + else + return CDK_Inv_Mode; + +leave: + if (rc) + { + cdk_stream_close (hd->fp); + hd->fp = NULL; + } + *ret_kr = hd->fp; + return rc; +} + + +static int +find_by_keyid (cdk_kbnode_t knode, cdk_dbsearch_t ks) +{ + cdk_kbnode_t node; + u32 keyid[2]; + + for (node = knode; node; node = node->next) + { + if (node->pkt->pkttype == CDK_PKT_PUBLIC_KEY || + node->pkt->pkttype == CDK_PKT_PUBLIC_SUBKEY || + node->pkt->pkttype == CDK_PKT_SECRET_KEY || + node->pkt->pkttype == CDK_PKT_SECRET_SUBKEY) + { + _cdk_pkt_get_keyid (node->pkt, keyid); + switch (ks->type) + { + case CDK_DBSEARCH_SHORT_KEYID: + if (keyid[1] == ks->u.keyid[1]) + return 1; + break; + + case CDK_DBSEARCH_KEYID: + if (KEYID_CMP (keyid, ks->u.keyid)) + return 1; + break; + + default: + _cdk_log_debug ("find_by_keyid: invalid mode = %d\n", ks->type); + return 0; + } + } + } + return 0; +} + + +static int +find_by_fpr (cdk_kbnode_t knode, cdk_dbsearch_t ks) +{ + cdk_kbnode_t node; + byte fpr[KEY_FPR_LEN]; + + if (ks->type != CDK_DBSEARCH_FPR) + return 0; + + for (node = knode; node; node = node->next) + { + if (node->pkt->pkttype == CDK_PKT_PUBLIC_KEY || + node->pkt->pkttype == CDK_PKT_PUBLIC_SUBKEY || + node->pkt->pkttype == CDK_PKT_SECRET_KEY || + node->pkt->pkttype == CDK_PKT_SECRET_SUBKEY) + { + _cdk_pkt_get_fingerprint (node->pkt, fpr); + if (!memcmp (ks->u.fpr, fpr, KEY_FPR_LEN)) + return 1; + break; + } + } + + return 0; +} + + +static int +find_by_pattern (cdk_kbnode_t knode, cdk_dbsearch_t ks) +{ + cdk_kbnode_t node; + size_t uidlen; + char *name; + + for (node = knode; node; node = node->next) + { + if (node->pkt->pkttype != CDK_PKT_USER_ID) + continue; + if (node->pkt->pkt.user_id->attrib_img != NULL) + continue; /* Skip attribute packets. */ + uidlen = node->pkt->pkt.user_id->len; + name = node->pkt->pkt.user_id->name; + switch (ks->type) + { + case CDK_DBSEARCH_EXACT: + if (name && + (strlen (ks->u.pattern) == uidlen && + !strncmp (ks->u.pattern, name, uidlen))) + return 1; + break; + + case CDK_DBSEARCH_SUBSTR: + if (uidlen > 65536) + break; + if (name && strlen (ks->u.pattern) > uidlen) + break; + if (name && _cdk_memistr (name, uidlen, ks->u.pattern)) + return 1; + break; + + default: /* Invalid mode */ + return 0; + } + } + return 0; +} + + +static void +keydb_search_free (cdk_dbsearch_t dbs) +{ + if (!dbs) + return; + if (dbs->type == CDK_DBSEARCH_EXACT || dbs->type == CDK_DBSEARCH_SUBSTR) + cdk_free (dbs->u.pattern); + dbs->type = 0; + cdk_free (dbs); +} + + +static void +keydb_cache_free (key_table_t cache) +{ + key_table_t c2; + + while (cache) + { + c2 = cache->next; + cache->offset = 0; + keydb_search_free (cache->desc); + cdk_free (cache); + cache = c2; + } +} + + +static key_table_t +keydb_cache_find (key_table_t cache, cdk_dbsearch_t desc) +{ + key_table_t t; + + for (t = cache; t; t = t->next) + { + if (t->desc->type != desc->type) + continue; + switch (t->desc->type) + { + case CDK_DBSEARCH_SHORT_KEYID: + case CDK_DBSEARCH_KEYID: + if (KEYID_CMP (t->desc->u.keyid, desc->u.keyid)) + return t; + break; + + case CDK_DBSEARCH_EXACT: + if (strlen (t->desc->u.pattern) == strlen (desc->u.pattern) && + !strcmp (t->desc->u.pattern, desc->u.pattern)) + return t; + break; + + case CDK_DBSEARCH_SUBSTR: + if (strstr (t->desc->u.pattern, desc->u.pattern)) + return t; + break; + + case CDK_DBSEARCH_FPR: + if (!memcmp (t->desc->u.fpr, desc->u.fpr, KEY_FPR_LEN)) + return t; + break; + } + } + + return NULL; +} + + +static cdk_error_t +keydb_cache_add (cdk_keydb_hd_t hd, cdk_dbsearch_t dbs, off_t offset) +{ + key_table_t k; + + if (!hd) + return CDK_Inv_Value; + + if (hd->ncache > KEYDB_CACHE_ENTRIES) + return 0; /* FIXME: we should replace the last entry. */ + k = cdk_calloc (1, sizeof *k); + if (!k) + return CDK_Out_Of_Core; + k->offset = offset; + keydb_search_copy (&k->desc, dbs); + k->next = hd->cache; + hd->cache = k; + hd->ncache++; + _cdk_log_debug ("cache: add entry off=%d type=%d\n", offset, dbs->type); + return 0; +} + + +static cdk_error_t +keydb_search_copy (cdk_dbsearch_t * r_dst, cdk_dbsearch_t src) +{ + cdk_dbsearch_t dst; + + if (!r_dst || !src) + return CDK_Inv_Value; + + *r_dst = NULL; + dst = cdk_calloc (1, sizeof *dst); + if (!dst) + return CDK_Out_Of_Core; + dst->type = src->type; + switch (src->type) + { + case CDK_DBSEARCH_EXACT: + case CDK_DBSEARCH_SUBSTR: + dst->u.pattern = cdk_strdup (src->u.pattern); + if (!dst->u.pattern) + return CDK_Out_Of_Core; + break; + + case CDK_DBSEARCH_SHORT_KEYID: + case CDK_DBSEARCH_KEYID: + dst->u.keyid[0] = src->u.keyid[0]; + dst->u.keyid[1] = src->u.keyid[1]; + break; + + case CDK_DBSEARCH_FPR: + memcpy (dst->u.fpr, src->u.fpr, KEY_FPR_LEN); + break; + } + *r_dst = dst; + return 0; +} + + +/** + * cdk_keydb_search_start: + * @db: key database handle + * @type: specifies the search type + * @desc: description which depends on the type + * + * Create a new keydb search object. + **/ +cdk_error_t +cdk_keydb_search_start (cdk_keydb_hd_t db, int type, void *desc) +{ + cdk_dbsearch_t dbs; + u32 *keyid; + char *p, tmp[3]; + int i; + + if (!db) + return CDK_Inv_Value; + if (type != CDK_DBSEARCH_NEXT && !desc) + return CDK_Inv_Mode; + + dbs = cdk_calloc (1, sizeof *dbs); + if (!dbs) + return CDK_Out_Of_Core; + dbs->type = type; + switch (type) + { + case CDK_DBSEARCH_EXACT: + case CDK_DBSEARCH_SUBSTR: + cdk_free (dbs->u.pattern); + dbs->u.pattern = cdk_strdup (desc); + if (!dbs->u.pattern) + { + cdk_free (dbs); + return CDK_Out_Of_Core; + } + break; + + case CDK_DBSEARCH_SHORT_KEYID: + keyid = desc; + dbs->u.keyid[1] = keyid[0]; + break; + + case CDK_DBSEARCH_KEYID: + keyid = desc; + dbs->u.keyid[0] = keyid[0]; + dbs->u.keyid[1] = keyid[1]; + break; + + case CDK_DBSEARCH_FPR: + memcpy (dbs->u.fpr, desc, KEY_FPR_LEN); + break; + + case CDK_DBSEARCH_NEXT: + break; + + case CDK_DBSEARCH_AUTO: + /* Override the type with the actual db search type. */ + dbs->type = classify_data (desc, strlen (desc)); + switch (dbs->type) + { + case CDK_DBSEARCH_SUBSTR: + case CDK_DBSEARCH_EXACT: + cdk_free (dbs->u.pattern); + p = dbs->u.pattern = cdk_strdup (desc); + if (!p) + { + cdk_free (dbs); + return CDK_Out_Of_Core; + } + break; + + case CDK_DBSEARCH_SHORT_KEYID: + case CDK_DBSEARCH_KEYID: + p = desc; + if (!strncmp (p, "0x", 2)) + p += 2; + if (strlen (p) == 8) + { + dbs->u.keyid[0] = 0; + dbs->u.keyid[1] = strtoul (p, NULL, 16); + } + else if (strlen (p) == 16) + { + dbs->u.keyid[0] = strtoul (p, NULL, 16); + dbs->u.keyid[1] = strtoul (p + 8, NULL, 16); + } + else + { /* Invalid key ID object. */ + cdk_free (dbs); + return CDK_Inv_Mode; + } + break; + + case CDK_DBSEARCH_FPR: + p = desc; + if (strlen (p) != 2 * KEY_FPR_LEN) + { + cdk_free (dbs); + return CDK_Inv_Mode; + } + for (i = 0; i < KEY_FPR_LEN; i++) + { + tmp[0] = p[2 * i]; + tmp[1] = p[2 * i + 1]; + tmp[2] = 0x00; + dbs->u.fpr[i] = strtoul (tmp, NULL, 16); + } + break; + } + break; + + default: + cdk_free (dbs); + _cdk_log_debug ("cdk_keydb_search_start: invalid mode = %d\n", type); + return CDK_Inv_Mode; + } + + keydb_search_free (db->dbs); + db->dbs = dbs; + return 0; +} + + +static cdk_error_t +keydb_pos_from_cache (cdk_keydb_hd_t hd, cdk_dbsearch_t ks, + int *r_cache_hit, off_t * r_off) +{ + key_table_t c; + + if (!hd || !r_cache_hit || !r_off) + return CDK_Inv_Value; + + /* Reset the values. */ + *r_cache_hit = 0; + *r_off = 0; + + c = keydb_cache_find (hd->cache, ks); + if (c != NULL) + { + _cdk_log_debug ("cache: found entry in cache.\n"); + *r_cache_hit = 1; + *r_off = c->offset; + return 0; + } + + /* No index cache available so we just return here. */ + if (!hd->idx) + return 0; + + if (hd->idx) + { + if (ks->type == CDK_DBSEARCH_KEYID) + { + if (keydb_idx_search (hd->idx, ks->u.keyid, NULL, r_off)) + return CDK_Error_No_Key; + _cdk_log_debug ("cache: found keyid entry in idx table.\n"); + *r_cache_hit = 1; + } + else if (ks->type == CDK_DBSEARCH_FPR) + { + if (keydb_idx_search (hd->idx, NULL, ks->u.fpr, r_off)) + return CDK_Error_No_Key; + _cdk_log_debug ("cache: found fpr entry in idx table.\n"); + *r_cache_hit = 1; + } + } + + return 0; +} + + +/** + * cdk_keydb_search: + * @hd: the keydb object + * @ks: the keydb search object + * @ret_key: kbnode object to store the key + * + * Search for a key in the given keyring. The search mode is handled + * via @ks. If the key was found, @ret_key contains the key data. + **/ +cdk_error_t +cdk_keydb_search (cdk_keydb_hd_t hd, cdk_kbnode_t * ret_key) +{ + cdk_stream_t kr; + cdk_kbnode_t knode; + cdk_dbsearch_t ks; + cdk_error_t rc = 0; + off_t pos = 0, off = 0; + int key_found = 0, cache_hit = 0; + + if (!hd || !ret_key) + return CDK_Inv_Value; + + *ret_key = NULL; + kr = NULL; + hd->search = 1; + rc = _cdk_keydb_open (hd, &kr); + if (rc) + return rc; + + if (!hd->no_cache) + { + /* It is possible the index is not up-to-date and thus we do + not find the requesed key. In this case, we reset cache hit + and continue our normal search procedure. */ + rc = keydb_pos_from_cache (hd, hd->dbs, &cache_hit, &off); + if (rc) + cache_hit = 0; + } + + knode = NULL; + ks = hd->dbs; + while (!key_found && !rc) + { + if (cache_hit && ks->type != CDK_DBSEARCH_NEXT) + cdk_stream_seek (kr, off); + pos = cdk_stream_tell (kr); + rc = cdk_keydb_get_keyblock (kr, &knode); + if (rc) + { + if (rc == CDK_EOF) + break; + else + return rc; + } + + switch (ks->type) + { + case CDK_DBSEARCH_SHORT_KEYID: + case CDK_DBSEARCH_KEYID: + key_found = find_by_keyid (knode, ks); + break; + + case CDK_DBSEARCH_FPR: + key_found = find_by_fpr (knode, ks); + break; + + case CDK_DBSEARCH_EXACT: + case CDK_DBSEARCH_SUBSTR: + key_found = find_by_pattern (knode, ks); + break; + + case CDK_DBSEARCH_NEXT: + key_found = knode ? 1 : 0; + break; + } + + if (key_found) + { + if (!keydb_cache_find (hd->cache, ks)) + keydb_cache_add (hd, ks, pos); + break; + } + + cdk_kbnode_release (knode); + knode = NULL; + } + + hd->search = 0; + if (key_found && rc == CDK_EOF) + rc = 0; + else if (rc == CDK_EOF && !key_found) + rc = CDK_Error_No_Key; + *ret_key = key_found ? knode : NULL; + return rc; +} + + +cdk_error_t +cdk_keydb_get_bykeyid (cdk_keydb_hd_t hd, u32 * keyid, cdk_kbnode_t * ret_key) +{ + cdk_error_t rc; + + if (!hd || !keyid || !ret_key) + return CDK_Inv_Value; + + rc = cdk_keydb_search_start (hd, CDK_DBSEARCH_KEYID, keyid); + if (!rc) + rc = cdk_keydb_search (hd, ret_key); + return rc; +} + + +cdk_error_t +cdk_keydb_get_byfpr (cdk_keydb_hd_t hd, const byte * fpr, + cdk_kbnode_t * r_key) +{ + cdk_error_t rc; + + if (!hd || !fpr || !r_key) + return CDK_Inv_Value; + + rc = cdk_keydb_search_start (hd, CDK_DBSEARCH_FPR, (byte *) fpr); + if (!rc) + rc = cdk_keydb_search (hd, r_key); + return rc; +} + + +cdk_error_t +cdk_keydb_get_bypattern (cdk_keydb_hd_t hd, const char *patt, + cdk_kbnode_t * ret_key) +{ + cdk_error_t rc; + + if (!hd || !patt || !ret_key) + return CDK_Inv_Value; + + rc = cdk_keydb_search_start (hd, CDK_DBSEARCH_SUBSTR, (char *) patt); + if (!rc) + rc = cdk_keydb_search (hd, ret_key); + return rc; +} + + +static int +keydb_check_key (cdk_packet_t pkt) +{ + cdk_pkt_pubkey_t pk; + int is_sk, valid; + + if (pkt->pkttype == CDK_PKT_PUBLIC_KEY || + pkt->pkttype == CDK_PKT_PUBLIC_SUBKEY) + { + pk = pkt->pkt.public_key; + is_sk = 0; + } + else if (pkt->pkttype == CDK_PKT_SECRET_KEY || + pkt->pkttype == CDK_PKT_SECRET_SUBKEY) + { + pk = pkt->pkt.secret_key->pk; + is_sk = 1; + } + else /* No key object. */ + return 0; + valid = !pk->is_revoked && !pk->has_expired; + if (is_sk) + return valid; + return valid && !pk->is_invalid; +} + + +/* Find the first kbnode with the requested packet type + that represents a valid key. */ +static cdk_kbnode_t +kbnode_find_valid (cdk_kbnode_t root, int pkttype) +{ + cdk_kbnode_t n; + + for (n = root; n; n = n->next) + { + if (n->pkt->pkttype != pkttype) + continue; + if (keydb_check_key (n->pkt)) + return n; + } + + return NULL; +} + + +static cdk_kbnode_t +keydb_find_byusage (cdk_kbnode_t root, int req_usage, int is_pk) +{ + cdk_kbnode_t node, key; + int req_type; + long timestamp; + + req_type = is_pk ? CDK_PKT_PUBLIC_KEY : CDK_PKT_SECRET_KEY; + if (!req_usage) + return kbnode_find_valid (root, req_type); + + node = cdk_kbnode_find (root, req_type); + if (node && !keydb_check_key (node->pkt)) + return NULL; + + key = NULL; + timestamp = 0; + /* We iteratre over the all nodes and search for keys or + subkeys which match the usage and which are not invalid. + A timestamp is used to figure out the newest valid key. */ + for (node = root; node; node = node->next) + { + if (is_pk && (node->pkt->pkttype == CDK_PKT_PUBLIC_KEY || + node->pkt->pkttype == CDK_PKT_PUBLIC_SUBKEY) + && keydb_check_key (node->pkt) + && (node->pkt->pkt.public_key->pubkey_usage & req_usage)) + { + if (node->pkt->pkt.public_key->timestamp > timestamp) + key = node; + } + if (!is_pk && (node->pkt->pkttype == CDK_PKT_SECRET_KEY || + node->pkt->pkttype == CDK_PKT_SECRET_SUBKEY) + && keydb_check_key (node->pkt) + && (node->pkt->pkt.secret_key->pk->pubkey_usage & req_usage)) + { + if (node->pkt->pkt.secret_key->pk->timestamp > timestamp) + key = node; + } + + } + return key; +} + + +static cdk_kbnode_t +keydb_find_bykeyid (cdk_kbnode_t root, const u32 * keyid, int search_mode) +{ + cdk_kbnode_t node; + u32 kid[2]; + + for (node = root; node; node = node->next) + { + if (!_cdk_pkt_get_keyid (node->pkt, kid)) + continue; + if (search_mode == CDK_DBSEARCH_SHORT_KEYID && kid[1] == keyid[1]) + return node; + else if (kid[0] == keyid[0] && kid[1] == keyid[1]) + return node; + } + return NULL; +} + + +cdk_error_t +_cdk_keydb_get_sk_byusage (cdk_keydb_hd_t hd, const char *name, + cdk_seckey_t * ret_sk, int usage) +{ + cdk_kbnode_t knode = NULL; + cdk_kbnode_t node, sk_node, pk_node; + cdk_pkt_seckey_t sk; + cdk_error_t rc; + const char *s; + int pkttype; + + if (!ret_sk || !usage) + return CDK_Inv_Value; + if (!hd) + return CDK_Error_No_Keyring; + + *ret_sk = NULL; + rc = cdk_keydb_search_start (hd, CDK_DBSEARCH_AUTO, (char *) name); + if (rc) + return rc; + + rc = cdk_keydb_search (hd, &knode); + if (rc) + return rc; + + sk_node = keydb_find_byusage (knode, usage, 0); + if (!sk_node) + { + cdk_kbnode_release (knode); + return CDK_Unusable_Key; + } + + /* We clone the node with the secret key to avoid that the + packet will be released. */ + _cdk_kbnode_clone (sk_node); + sk = sk_node->pkt->pkt.secret_key; + + for (node = knode; node; node = node->next) + { + if (node->pkt->pkttype == CDK_PKT_USER_ID) + { + s = node->pkt->pkt.user_id->name; + if (sk && !sk->pk->uid && _cdk_memistr (s, strlen (s), name)) + { + _cdk_copy_userid (&sk->pk->uid, node->pkt->pkt.user_id); + break; + } + } + } + + /* To find the self signature, we need the primary public key because + the selected secret key might be different from the primary key. */ + pk_node = cdk_kbnode_find (knode, CDK_PKT_SECRET_KEY); + if (!pk_node) + { + cdk_kbnode_release (knode); + return CDK_Unusable_Key; + } + node = find_selfsig_node (knode, pk_node->pkt->pkt.secret_key->pk); + if (sk->pk->uid && node) + _cdk_copy_signature (&sk->pk->uid->selfsig, node->pkt->pkt.signature); + + /* We only release the outer packet. */ + _cdk_pkt_detach_free (sk_node->pkt, &pkttype, (void *) &sk); + cdk_kbnode_release (knode); + *ret_sk = sk; + return rc; +} + + +cdk_error_t +_cdk_keydb_get_pk_byusage (cdk_keydb_hd_t hd, const char *name, + cdk_pubkey_t * ret_pk, int usage) +{ + cdk_kbnode_t knode, node, pk_node; + cdk_pkt_pubkey_t pk; + const char *s; + cdk_error_t rc; + + if (!ret_pk || !usage) + return CDK_Inv_Value; + if (!hd) + return CDK_Error_No_Keyring; + + *ret_pk = NULL; + rc = cdk_keydb_search_start (hd, CDK_DBSEARCH_AUTO, (char *) name); + if (!rc) + rc = cdk_keydb_search (hd, &knode); + if (rc) + return rc; + + node = keydb_find_byusage (knode, usage, 1); + if (!node) + { + cdk_kbnode_release (knode); + return CDK_Unusable_Key; + } + + pk = NULL; + _cdk_copy_pubkey (&pk, node->pkt->pkt.public_key); + for (node = knode; node; node = node->next) + { + if (node->pkt->pkttype == CDK_PKT_USER_ID) + { + s = node->pkt->pkt.user_id->name; + if (pk && !pk->uid && _cdk_memistr (s, strlen (s), name)) + { + _cdk_copy_userid (&pk->uid, node->pkt->pkt.user_id); + break; + } + } + } + + /* Same as in the sk code, the selected key can be a sub key + and thus we need the primary key to find the self sig. */ + pk_node = cdk_kbnode_find (knode, CDK_PKT_PUBLIC_KEY); + if (!pk_node) + { + cdk_kbnode_release (knode); + return CDK_Unusable_Key; + } + node = find_selfsig_node (knode, pk_node->pkt->pkt.public_key); + if (pk->uid && node) + _cdk_copy_signature (&pk->uid->selfsig, node->pkt->pkt.signature); + cdk_kbnode_release (knode); + + *ret_pk = pk; + return rc; +} + + +/** + * cdk_keydb_get_pk: + * @hd: key db handle + * @keyid: keyid of the key + * @r_pk: the allocated public key + * + * Perform a key database search by keyid and return the raw public + * key without any signatures or user id's. + **/ +cdk_error_t +cdk_keydb_get_pk (cdk_keydb_hd_t hd, u32 * keyid, cdk_pubkey_t * r_pk) +{ + cdk_kbnode_t knode = NULL, node; + cdk_pubkey_t pk; + cdk_error_t rc; + size_t s_type; + int pkttype; + + if (!keyid || !r_pk) + return CDK_Inv_Value; + if (!hd) + return CDK_Error_No_Keyring; + + *r_pk = NULL; + s_type = !keyid[0] ? CDK_DBSEARCH_SHORT_KEYID : CDK_DBSEARCH_KEYID; + rc = cdk_keydb_search_start (hd, s_type, keyid); + if (rc) + return rc; + rc = cdk_keydb_search (hd, &knode); + if (rc) + return rc; + + node = keydb_find_bykeyid (knode, keyid, s_type); + if (!node) + { + cdk_kbnode_release (knode); + return CDK_Error_No_Key; + } + + /* See comment in cdk_keydb_get_sk() */ + _cdk_pkt_detach_free (node->pkt, &pkttype, (void *) &pk); + *r_pk = pk; + _cdk_kbnode_clone (node); + cdk_kbnode_release (knode); + + return rc; +} + + +/** + * cdk_keydb_get_sk: + * @hd: key db handle + * @keyid: the keyid of the key + * @ret_sk: the allocated secret key + * + * Perform a key database search by keyid and return + * only the raw secret key without the additional nodes, + * like the user id or the signatures. + **/ +cdk_error_t +cdk_keydb_get_sk (cdk_keydb_hd_t hd, u32 * keyid, cdk_seckey_t * ret_sk) +{ + cdk_kbnode_t snode, node; + cdk_seckey_t sk; + cdk_error_t rc; + int pkttype; + + if (!keyid || !ret_sk) + return CDK_Inv_Value; + if (!hd) + return CDK_Error_No_Keyring; + + *ret_sk = NULL; + rc = cdk_keydb_get_bykeyid (hd, keyid, &snode); + if (rc) + return rc; + + node = keydb_find_bykeyid (snode, keyid, CDK_DBSEARCH_KEYID); + if (!node) + { + cdk_kbnode_release (snode); + return CDK_Error_No_Key; + } + + /* We need to release the packet itself but not its contents + and thus we detach the openpgp packet and release the structure. */ + _cdk_pkt_detach_free (node->pkt, &pkttype, (void *) &sk); + _cdk_kbnode_clone (node); + cdk_kbnode_release (snode); + + *ret_sk = sk; + return 0; +} + + +static int +is_selfsig (cdk_kbnode_t node, const u32 * keyid) +{ + cdk_pkt_signature_t sig; + + if (node->pkt->pkttype != CDK_PKT_SIGNATURE) + return 0; + sig = node->pkt->pkt.signature; + if ((sig->sig_class >= 0x10 && sig->sig_class <= 0x13) && + sig->keyid[0] == keyid[0] && sig->keyid[1] == keyid[1]) + return 1; + + return 0; +} + + +/* Find the newest self signature for the public key @pk + and return the signature node. */ +static cdk_kbnode_t +find_selfsig_node (cdk_kbnode_t key, cdk_pkt_pubkey_t pk) +{ + cdk_kbnode_t n, sig; + unsigned int ts; + u32 keyid[2]; + + cdk_pk_get_keyid (pk, keyid); + sig = NULL; + ts = 0; + for (n = key; n; n = n->next) + { + if (is_selfsig (n, keyid) && n->pkt->pkt.signature->timestamp > ts) + { + ts = n->pkt->pkt.signature->timestamp; + sig = n; + } + } + + return sig; +} + + + +static cdk_error_t +keydb_merge_selfsig (cdk_kbnode_t key, u32 * keyid) +{ + cdk_kbnode_t node, kbnode, unode; + cdk_subpkt_t s = NULL; + cdk_pkt_signature_t sig = NULL; + cdk_pkt_userid_t uid = NULL; + const byte *symalg = NULL, *hashalg = NULL, *compalg = NULL; + size_t nsymalg = 0, nhashalg = 0, ncompalg = 0, n = 0; + size_t key_usage = 0, key_expire = 0; + + if (!key) + return CDK_Inv_Value; + + for (node = key; node; node = node->next) + { + if (!is_selfsig (node, keyid)) + continue; + unode = cdk_kbnode_find_prev (key, node, CDK_PKT_USER_ID); + if (!unode) + return CDK_Error_No_Key; + uid = unode->pkt->pkt.user_id; + sig = node->pkt->pkt.signature; + s = cdk_subpkt_find (sig->hashed, CDK_SIGSUBPKT_PRIMARY_UID); + if (s) + uid->is_primary = 1; + s = cdk_subpkt_find (sig->hashed, CDK_SIGSUBPKT_FEATURES); + if (s && s->size == 1 && s->d[0] & 0x01) + uid->mdc_feature = 1; + s = cdk_subpkt_find (sig->hashed, CDK_SIGSUBPKT_KEY_EXPIRE); + if (s && s->size == 4) + key_expire = _cdk_buftou32 (s->d); + s = cdk_subpkt_find (sig->hashed, CDK_SIGSUBPKT_KEY_FLAGS); + if (s) + { + if (s->d[0] & 0x03) /* cert + sign data */ + key_usage |= CDK_KEY_USG_SIGN; + if (s->d[0] & 0x0C) /* encrypt comm. + storage */ + key_usage |= CDK_KEY_USG_ENCR; + if (s->d[0] & 0x20) + key_usage |= CDK_KEY_USG_AUTH; + } + s = cdk_subpkt_find (sig->hashed, CDK_SIGSUBPKT_PREFS_SYM); + if (s) + { + symalg = s->d; + nsymalg = s->size; + n += s->size + 1; + } + s = cdk_subpkt_find (sig->hashed, CDK_SIGSUBPKT_PREFS_HASH); + if (s) + { + hashalg = s->d; + nhashalg = s->size; + n += s->size + 1; + } + s = cdk_subpkt_find (sig->hashed, CDK_SIGSUBPKT_PREFS_ZIP); + if (s) + { + compalg = s->d; + ncompalg = s->size; + n += s->size + 1; + } + if (uid->prefs != NULL) + cdk_free (uid->prefs); + if (!n || !hashalg || !compalg || !symalg) + uid->prefs = NULL; + else + { + uid->prefs = cdk_calloc (1, sizeof (*uid->prefs) * (n + 1)); + if (!uid->prefs) + return CDK_Out_Of_Core; + n = 0; + for (; nsymalg; nsymalg--, n++) + { + uid->prefs[n].type = CDK_PREFTYPE_SYM; + uid->prefs[n].value = *symalg++; + } + for (; nhashalg; nhashalg--, n++) + { + uid->prefs[n].type = CDK_PREFTYPE_HASH; + uid->prefs[n].value = *hashalg++; + } + for (; ncompalg; ncompalg--, n++) + { + uid->prefs[n].type = CDK_PREFTYPE_ZIP; + uid->prefs[n].value = *compalg++; + } + + uid->prefs[n].type = CDK_PREFTYPE_NONE; /* end of list marker */ + uid->prefs[n].value = 0; + uid->prefs_size = n; + } + } + + /* Now we add the extracted information to the primary key. */ + kbnode = cdk_kbnode_find (key, CDK_PKT_PUBLIC_KEY); + if (kbnode) + { + cdk_pkt_pubkey_t pk = kbnode->pkt->pkt.public_key; + if (uid && uid->prefs && n) + { + if (pk->prefs != NULL) + cdk_free (pk->prefs); + pk->prefs = _cdk_copy_prefs (uid->prefs); + pk->prefs_size = n; + } + if (key_expire) + { + pk->expiredate = pk->timestamp + key_expire; + pk->has_expired = pk->expiredate > (u32) time (NULL) ? 0 : 1; + } + + if (key_usage) + pk->pubkey_usage = key_usage; + pk->is_invalid = 0; + } + + return 0; +} + + +static cdk_error_t +keydb_parse_allsigs (cdk_kbnode_t knode, cdk_keydb_hd_t hd, int check) +{ + cdk_kbnode_t node, kb; + cdk_pkt_signature_t sig; + cdk_pkt_pubkey_t pk; + cdk_subpkt_t s = NULL; + u32 expiredate = 0, curtime = (u32) time (NULL); + u32 keyid[2]; + + if (!knode) + return CDK_Inv_Value; + if (check && !hd) + return CDK_Inv_Mode; + + kb = cdk_kbnode_find (knode, CDK_PKT_SECRET_KEY); + if (kb) + return 0; + + /* Reset */ + for (node = knode; node; node = node->next) + { + if (node->pkt->pkttype == CDK_PKT_USER_ID) + node->pkt->pkt.user_id->is_revoked = 0; + else if (node->pkt->pkttype == CDK_PKT_PUBLIC_KEY || + node->pkt->pkttype == CDK_PKT_PUBLIC_SUBKEY) + node->pkt->pkt.public_key->is_revoked = 0; + } + + kb = cdk_kbnode_find (knode, CDK_PKT_PUBLIC_KEY); + if (!kb) + return CDK_Wrong_Format; + cdk_pk_get_keyid (kb->pkt->pkt.public_key, keyid); + + for (node = knode; node; node = node->next) + { + if (node->pkt->pkttype == CDK_PKT_SIGNATURE) + { + sig = node->pkt->pkt.signature; + /* Revocation certificates for primary keys */ + if (sig->sig_class == 0x20) + { + kb = cdk_kbnode_find_prev (knode, node, CDK_PKT_PUBLIC_KEY); + if (kb) + { + kb->pkt->pkt.public_key->is_revoked = 1; + if (check) + _cdk_pk_check_sig (hd, kb, node, NULL); + } + else + return CDK_Error_No_Key; + } + /* Revocation certificates for subkeys */ + else if (sig->sig_class == 0x28) + { + kb = cdk_kbnode_find_prev (knode, node, CDK_PKT_PUBLIC_SUBKEY); + if (kb) + { + kb->pkt->pkt.public_key->is_revoked = 1; + if (check) + _cdk_pk_check_sig (hd, kb, node, NULL); + } + else + return CDK_Error_No_Key; + } + /* Revocation certifcates for user ID's */ + else if (sig->sig_class == 0x30) + { + if (sig->keyid[0] != keyid[0] || sig->keyid[1] != keyid[1]) + continue; /* revokes an earlier signature, no userID. */ + kb = cdk_kbnode_find_prev (knode, node, CDK_PKT_USER_ID); + if (kb) + { + kb->pkt->pkt.user_id->is_revoked = 1; + if (check) + _cdk_pk_check_sig (hd, kb, node, NULL); + } + else + return CDK_Error_No_Key; + } + /* Direct certificates for primary keys */ + else if (sig->sig_class == 0x1F) + { + kb = cdk_kbnode_find_prev (knode, node, CDK_PKT_PUBLIC_KEY); + if (kb) + { + pk = kb->pkt->pkt.public_key; + pk->is_invalid = 0; + s = cdk_subpkt_find (node->pkt->pkt.signature->hashed, + CDK_SIGSUBPKT_KEY_EXPIRE); + if (s) + { + expiredate = _cdk_buftou32 (s->d); + pk->expiredate = pk->timestamp + expiredate; + pk->has_expired = pk->expiredate > curtime ? 0 : 1; + } + if (check) + _cdk_pk_check_sig (hd, kb, node, NULL); + } + else + return CDK_Error_No_Key; + } + /* Direct certificates for subkeys */ + else if (sig->sig_class == 0x18) + { + kb = cdk_kbnode_find_prev (knode, node, CDK_PKT_PUBLIC_SUBKEY); + if (kb) + { + pk = kb->pkt->pkt.public_key; + pk->is_invalid = 0; + s = cdk_subpkt_find (node->pkt->pkt.signature->hashed, + CDK_SIGSUBPKT_KEY_EXPIRE); + if (s) + { + expiredate = _cdk_buftou32 (s->d); + pk->expiredate = pk->timestamp + expiredate; + pk->has_expired = pk->expiredate > curtime ? 0 : 1; + } + if (check) + _cdk_pk_check_sig (hd, kb, node, NULL); + } + else + return CDK_Error_No_Key; + } + } + } + node = cdk_kbnode_find (knode, CDK_PKT_PUBLIC_KEY); + if (node && node->pkt->pkt.public_key->version == 3) + { + /* v3 public keys have no additonal signatures for the key directly. + we say the key is valid when we have at least a self signature. */ + pk = node->pkt->pkt.public_key; + for (node = knode; node; node = node->next) + { + if (is_selfsig (node, keyid)) + { + pk->is_invalid = 0; + break; + } + } + } + if (node && (node->pkt->pkt.public_key->is_revoked || + node->pkt->pkt.public_key->has_expired)) + { + /* If the primary key has been revoked, mark all subkeys as invalid + because without a primary key they are not useable */ + for (node = knode; node; node = node->next) + { + if (node->pkt->pkttype == CDK_PKT_PUBLIC_SUBKEY) + node->pkt->pkt.public_key->is_invalid = 1; + } + } + + return 0; +} + + +cdk_error_t +cdk_keydb_get_keyblock (cdk_stream_t inp, cdk_kbnode_t * r_knode) +{ + cdk_packet_t pkt; + cdk_kbnode_t knode, node; + cdk_desig_revoker_t revkeys; + cdk_error_t rc; + u32 keyid[2], main_keyid[2]; + off_t old_off; + int key_seen, got_key; + + if (!inp || !r_knode) + return CDK_Inv_Value; + + /* Reset all values. */ + keyid[0] = keyid[1] = 0; + main_keyid[0] = main_keyid[1] = 0; + revkeys = NULL; + knode = NULL; + key_seen = got_key = 0; + + *r_knode = NULL; + rc = CDK_EOF; + while (!cdk_stream_eof (inp)) + { + cdk_pkt_new (&pkt); + old_off = cdk_stream_tell (inp); + rc = cdk_pkt_read (inp, pkt); + if (rc) + { + cdk_pkt_release (pkt); + if (rc == CDK_EOF) + break; + else + { /* Release all packets we reached so far. */ + _cdk_log_debug ("keydb_get_keyblock: error %d\n", rc); + cdk_kbnode_release (knode); + return rc; + } + } + if (pkt->pkttype == CDK_PKT_PUBLIC_KEY || + pkt->pkttype == CDK_PKT_PUBLIC_SUBKEY || + pkt->pkttype == CDK_PKT_SECRET_KEY || + pkt->pkttype == CDK_PKT_SECRET_SUBKEY) + { + if (key_seen && (pkt->pkttype == CDK_PKT_PUBLIC_KEY || + pkt->pkttype == CDK_PKT_SECRET_KEY)) + { + /* The next key starts here so set the file pointer + and leave the loop. */ + cdk_stream_seek (inp, old_off); + cdk_pkt_release (pkt); + break; + } + if (pkt->pkttype == CDK_PKT_PUBLIC_KEY || + pkt->pkttype == CDK_PKT_SECRET_KEY) + { + _cdk_pkt_get_keyid (pkt, main_keyid); + key_seen = 1; + } + else if (pkt->pkttype == CDK_PKT_PUBLIC_SUBKEY || + pkt->pkttype == CDK_PKT_SECRET_SUBKEY) + { + if (pkt->pkttype == CDK_PKT_PUBLIC_SUBKEY) + { + pkt->pkt.public_key->main_keyid[0] = main_keyid[0]; + pkt->pkt.public_key->main_keyid[1] = main_keyid[1]; + } + else + { + pkt->pkt.secret_key->main_keyid[0] = main_keyid[0]; + pkt->pkt.secret_key->main_keyid[1] = main_keyid[1]; + } + } + /* We save this for the signature */ + _cdk_pkt_get_keyid (pkt, keyid); + got_key = 1; + } + else if (pkt->pkttype == CDK_PKT_USER_ID) + ; + else if (pkt->pkttype == CDK_PKT_SIGNATURE) + { + pkt->pkt.signature->key[0] = keyid[0]; + pkt->pkt.signature->key[1] = keyid[1]; + if (pkt->pkt.signature->sig_class == 0x1F && + pkt->pkt.signature->revkeys) + revkeys = pkt->pkt.signature->revkeys; + } + node = cdk_kbnode_new (pkt); + if (!knode) + knode = node; + else + _cdk_kbnode_add (knode, node); + } + + if (got_key) + { + keydb_merge_selfsig (knode, main_keyid); + rc = keydb_parse_allsigs (knode, NULL, 0); + if (revkeys) + { + node = cdk_kbnode_find (knode, CDK_PKT_PUBLIC_KEY); + if (node) + node->pkt->pkt.public_key->revkeys = revkeys; + } + } + else + cdk_kbnode_release (knode); + *r_knode = got_key ? knode : NULL; + + /* It is possible that we are in an EOF condition after we + successfully read a keyblock. For example if the requested + key is the last in the file. */ + if (rc == CDK_EOF && got_key) + rc = 0; + return rc; +} + + +/* Return the type of the given data. In case it cannot be classified, + a substring search will be performed. */ +static int +classify_data (const byte * buf, size_t len) +{ + int type; + int i; + + if (buf[0] == '0' && (buf[1] == 'x' || buf[1] == 'X')) + { /* Skip hex prefix. */ + buf += 2; + len -= 2; + } + + /* The length of the data does not match either a keyid or a fingerprint. */ + if (len != 8 && len != 16 && len != 40) + return CDK_DBSEARCH_SUBSTR; + + for (i = 0; i < len; i++) + { + if (!isxdigit (buf[i])) + return CDK_DBSEARCH_SUBSTR; + } + if (i != len) + return CDK_DBSEARCH_SUBSTR; + switch (len) + { + case 8: + type = CDK_DBSEARCH_SHORT_KEYID; + break; + case 16: + type = CDK_DBSEARCH_KEYID; + break; + case 40: + type = CDK_DBSEARCH_FPR; + break; + default: + type = CDK_DBSEARCH_SUBSTR; + break; + } + + return type; +} + + +/** + * cdk_keydb_export: + * @hd: the keydb handle + * @out: the output stream + * @remusr: the list of key pattern to export + * + * Export a list of keys to the given output stream. + * Use string list with names for pattering searching. + * This procedure strips local signatures. + **/ +cdk_error_t +cdk_keydb_export (cdk_keydb_hd_t hd, cdk_stream_t out, cdk_strlist_t remusr) +{ + cdk_kbnode_t knode, node; + cdk_strlist_t r; + cdk_error_t rc; + int old_ctb; + + for (r = remusr; r; r = r->next) + { + rc = cdk_keydb_search_start (hd, CDK_DBSEARCH_AUTO, r->d); + if (rc) + return rc; + rc = cdk_keydb_search (hd, &knode); + if (rc) + return rc; + node = cdk_kbnode_find (knode, CDK_PKT_PUBLIC_KEY); + if (!node) + return CDK_Error_No_Key; + + /* If the key is a version 3 key, use the old packet + format for the output. */ + if (node->pkt->pkt.public_key->version == 3) + old_ctb = 1; + else + old_ctb = 0; + + for (node = knode; node; node = node->next) + { + /* No specified format; skip them */ + if (node->pkt->pkttype == CDK_PKT_RING_TRUST) + continue; + /* We never export local signed signatures */ + if (node->pkt->pkttype == CDK_PKT_SIGNATURE && + !node->pkt->pkt.signature->flags.exportable) + continue; + /* Filter out invalid signatures */ + if (node->pkt->pkttype == CDK_PKT_SIGNATURE && + !KEY_CAN_SIGN (node->pkt->pkt.signature->pubkey_algo)) + continue; + + /* Adjust the ctb flag if needed. */ + node->pkt->old_ctb = old_ctb; + rc = cdk_pkt_write (out, node->pkt); + if (rc) + { + cdk_kbnode_release (knode); + return rc; + } + } + cdk_kbnode_release (knode); + knode = NULL; + } + return 0; +} + + +static cdk_packet_t +find_key_packet (cdk_kbnode_t knode, int *r_is_sk) +{ + cdk_packet_t pkt; + + pkt = cdk_kbnode_find_packet (knode, CDK_PKT_PUBLIC_KEY); + if (!pkt) + { + pkt = cdk_kbnode_find_packet (knode, CDK_PKT_SECRET_KEY); + if (r_is_sk) + *r_is_sk = pkt ? 1 : 0; + } + return pkt; +} + + +/* Return 1 if the is allowd in a key node. */ +static int +is_key_node (cdk_kbnode_t node) +{ + switch (node->pkt->pkttype) + { + case CDK_PKT_SIGNATURE: + case CDK_PKT_SECRET_KEY: + case CDK_PKT_PUBLIC_KEY: + case CDK_PKT_SECRET_SUBKEY: + case CDK_PKT_PUBLIC_SUBKEY: + case CDK_PKT_USER_ID: + case CDK_PKT_ATTRIBUTE: + return 1; + + default: + return 0; + } + + return 0; +} + + +cdk_error_t +cdk_keydb_import (cdk_keydb_hd_t hd, cdk_kbnode_t knode) +{ + cdk_kbnode_t node, chk; + cdk_packet_t pkt; + cdk_stream_t out; + cdk_error_t rc; + u32 keyid[2]; + + if (!hd || !knode) + return CDK_Inv_Value; + + pkt = find_key_packet (knode, NULL); + if (!pkt) + return CDK_Inv_Packet; + + _cdk_pkt_get_keyid (pkt, keyid); + chk = NULL; + cdk_keydb_get_bykeyid (hd, keyid, &chk); + if (chk) + { /* FIXME: search for new signatures */ + cdk_kbnode_release (chk); + return 0; + } + + /* We append data to the stream so we need to close + the stream here to re-open it later. */ + if (hd->fp) + { + cdk_stream_close (hd->fp); + hd->fp = NULL; + } + + rc = _cdk_stream_append (hd->name, &out); + if (rc) + return rc; + + for (node = knode; node; node = node->next) + { + if (node->pkt->pkttype == CDK_PKT_RING_TRUST) + continue; /* No uniformed syntax for this packet */ + if (node->pkt->pkttype == CDK_PKT_SIGNATURE && + !node->pkt->pkt.signature->flags.exportable) + { + _cdk_log_debug ("key db import: skip local signature\n"); + continue; + } + + if (!is_key_node (node)) + { + _cdk_log_debug ("key db import: skip invalid node of type %d\n", + node->pkt->pkttype); + continue; + } + + rc = cdk_pkt_write (out, node->pkt); + if (rc) + { + cdk_stream_close (out); + return rc; + } + } + + cdk_stream_close (out); + if (!hd->no_cache) + cdk_keydb_idx_rebuild (hd); + hd->stats.new_keys++; + + return 0; +} + + +cdk_error_t +_cdk_keydb_check_userid (cdk_keydb_hd_t hd, u32 * keyid, const char *id) +{ + cdk_kbnode_t knode = NULL, unode = NULL; + cdk_error_t rc; + int check; + + if (!hd) + return CDK_Inv_Value; + + rc = cdk_keydb_search_start (hd, CDK_DBSEARCH_KEYID, keyid); + if (rc) + return rc; + rc = cdk_keydb_search (hd, &knode); + if (rc) + return rc; + + rc = cdk_keydb_search_start (hd, CDK_DBSEARCH_EXACT, (char *) id); + if (!rc) + rc = cdk_keydb_search (hd, &unode); + if (rc) + { + cdk_kbnode_release (knode); + return rc; + } + + check = 0; + cdk_keydb_search_start (hd, CDK_DBSEARCH_KEYID, keyid); + if (unode && find_by_keyid (unode, hd->dbs)) + check++; + cdk_kbnode_release (unode); + + cdk_keydb_search_start (hd, CDK_DBSEARCH_EXACT, (char *) id); + if (knode && find_by_pattern (knode, hd->dbs)) + check++; + cdk_kbnode_release (knode); + + return check == 2 ? 0 : CDK_Inv_Value; +} + + +/** + * cdk_keydb_check_sk: + * @hd: the key db handle + * @keyid: the 64-bit keyid + * + * Check if a secret key with the given key ID is available + * in the key database. + **/ +cdk_error_t +cdk_keydb_check_sk (cdk_keydb_hd_t hd, u32 * keyid) +{ + cdk_stream_t db; + cdk_packet_t pkt; + cdk_error_t rc; + u32 kid[2]; + + if (!hd || !keyid) + return CDK_Inv_Value; + if (!hd->secret) + return CDK_Inv_Mode; + + rc = _cdk_keydb_open (hd, &db); + if (rc) + return rc; + cdk_pkt_new (&pkt); + while (!cdk_pkt_read (db, pkt)) + { + if (pkt->pkttype != CDK_PKT_SECRET_KEY && + pkt->pkttype != CDK_PKT_SECRET_SUBKEY) + { + cdk_pkt_free (pkt); + continue; + } + cdk_sk_get_keyid (pkt->pkt.secret_key, kid); + if (KEYID_CMP (kid, keyid)) + { + cdk_pkt_release (pkt); + return 0; + } + cdk_pkt_free (pkt); + } + cdk_pkt_release (pkt); + return CDK_Error_No_Key; +} + + +/** + * cdk_listkey_start: + * @r_ctx: pointer to store the new context + * @db: the key database handle + * @patt: string pattern + * @fpatt: recipients from a stringlist to show + * + * Prepare a key listing with the given parameters. Two modes are supported. + * The first mode uses string pattern to determine if the key should be + * returned or not. The other mode uses a string list to request the key + * which should be listed. + **/ +cdk_error_t +cdk_listkey_start (cdk_listkey_t * r_ctx, cdk_keydb_hd_t db, + const char *patt, cdk_strlist_t fpatt) +{ + cdk_listkey_t ctx; + cdk_stream_t inp; + cdk_error_t rc; + + if (!r_ctx || !db) + return CDK_Inv_Value; + if ((patt && fpatt) || (!patt && !fpatt)) + return CDK_Inv_Mode; + rc = _cdk_keydb_open (db, &inp); + if (rc) + return rc; + ctx = cdk_calloc (1, sizeof *ctx); + if (!ctx) + return CDK_Out_Of_Core; + ctx->db = db; + ctx->inp = inp; + if (patt) + { + ctx->u.patt = cdk_strdup (patt); + if (!ctx->u.patt) + return CDK_Out_Of_Core; + } + else if (fpatt) + { + cdk_strlist_t l; + for (l = fpatt; l; l = l->next) + cdk_strlist_add (&ctx->u.fpatt, l->d); + } + ctx->type = patt ? 1 : 0; + ctx->init = 1; + *r_ctx = ctx; + return 0; +} + + +/** + * cdk_listkey_close: + * @ctx: the list key context + * + * Free the list key context. + **/ +void +cdk_listkey_close (cdk_listkey_t ctx) +{ + if (!ctx) + return; + + if (ctx->type) + cdk_free (ctx->u.patt); + else + cdk_strlist_free (ctx->u.fpatt); + cdk_free (ctx); +} + + +/** + * cdk_listkey_next: + * @ctx: list key context + * @r_key: the pointer to the new key node object + * + * Retrieve the next key from the pattern of the key list context. + **/ +cdk_error_t +cdk_listkey_next (cdk_listkey_t ctx, cdk_kbnode_t * ret_key) +{ + if (!ctx || !ret_key) + return CDK_Inv_Value; + if (!ctx->init) + return CDK_Inv_Mode; + + if (ctx->type && ctx->u.patt[0] == '*') + return cdk_keydb_get_keyblock (ctx->inp, ret_key); + else if (ctx->type) + { + cdk_kbnode_t node; + struct cdk_dbsearch_s ks; + cdk_error_t rc; + + for (;;) + { + rc = cdk_keydb_get_keyblock (ctx->inp, &node); + if (rc) + return rc; + memset (&ks, 0, sizeof (ks)); + ks.type = CDK_DBSEARCH_SUBSTR; + ks.u.pattern = ctx->u.patt; + if (find_by_pattern (node, &ks)) + { + *ret_key = node; + return 0; + } + cdk_kbnode_release (node); + node = NULL; + } + } + else + { + if (!ctx->t) + ctx->t = ctx->u.fpatt; + else if (ctx->t->next) + ctx->t = ctx->t->next; + else + return CDK_EOF; + return cdk_keydb_get_bypattern (ctx->db, ctx->t->d, ret_key); + } + return CDK_General_Error; +} + + +int +_cdk_keydb_is_secret (cdk_keydb_hd_t db) +{ + return db->secret; +} diff --git a/src/daemon/https/opencdk/literal.c b/src/daemon/https/opencdk/literal.c new file mode 100644 index 00000000..4ccba6b2 --- /dev/null +++ b/src/daemon/https/opencdk/literal.c @@ -0,0 +1,307 @@ +/* Literal.c - Literal packet filters + * Copyright (C) 2002, 2003 Timo Schulz + * + * This file is part of OpenCDK. + * + * OpenCDK 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. + * + * OpenCDK 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. + */ +#ifdef HAVE_CONFIG_H +# include +#endif +#include +#include + +#include "opencdk.h" +#include "main.h" +#include "filters.h" + + +/* Duplicate the string @s but strip of possible + relative folder names of it. */ +static char * +dup_trim_filename (const char *s) +{ + char *p = NULL; + + p = strrchr (s, '/'); + if (!p) + p = strrchr (s, '\\'); + if (!p) + return cdk_strdup (s); + return cdk_strdup (p + 1); +} + + +static cdk_error_t +literal_decode (void *opaque, FILE * in, FILE * out) +{ + literal_filter_t *pfx = opaque; + cdk_stream_t si, so; + cdk_packet_t pkt; + cdk_pkt_literal_t pt; + byte buf[BUFSIZE]; + size_t nread; + int bufsize; + cdk_error_t rc; + + _cdk_log_debug ("literal filter: decode\n"); + + if (!pfx || !in || !out) + return CDK_Inv_Value; + + rc = _cdk_stream_fpopen (in, STREAMCTL_READ, &si); + if (rc) + return rc; + + cdk_pkt_new (&pkt); + rc = cdk_pkt_read (si, pkt); + if (rc || pkt->pkttype != CDK_PKT_LITERAL) + { + cdk_pkt_release (pkt); + cdk_stream_close (si); + return !rc ? CDK_Inv_Packet : rc; + } + + rc = _cdk_stream_fpopen (out, STREAMCTL_WRITE, &so); + if (rc) + { + cdk_pkt_release (pkt); + cdk_stream_close (si); + return rc; + } + + pt = pkt->pkt.literal; + pfx->mode = pt->mode; + + if (pfx->filename && pt->namelen > 0) + { + /* The name in the literal packet is more authorative. */ + cdk_free (pfx->filename); + pfx->filename = dup_trim_filename (pt->name); + } + else if (!pfx->filename && pt->namelen > 0) + pfx->filename = dup_trim_filename (pt->name); + else if (!pt->namelen && !pfx->filename && pfx->orig_filename) + { + /* In this case, we need to derrive the output file name + from the original name and cut off the OpenPGP extension. + If this is not possible, we return an error. */ + if (!stristr (pfx->orig_filename, ".gpg") && + !stristr (pfx->orig_filename, ".pgp") && + !stristr (pfx->orig_filename, ".asc")) + { + cdk_pkt_release (pkt); + cdk_stream_close (si); + cdk_stream_close (so); + _cdk_log_debug + ("literal filter: no file name and no PGP extension\n"); + return CDK_Inv_Mode; + } + _cdk_log_debug ("literal filter: derrive file name from original\n"); + pfx->filename = dup_trim_filename (pfx->orig_filename); + pfx->filename[strlen (pfx->filename) - 4] = '\0'; + } + + while (!feof (in)) + { + _cdk_log_debug ("literal_decode: part on %d size %lu\n", + pfx->blkmode.on, pfx->blkmode.size); + if (pfx->blkmode.on) + bufsize = pfx->blkmode.size; + else + bufsize = pt->len < DIM (buf) ? pt->len : DIM (buf); + nread = cdk_stream_read (pt->buf, buf, bufsize); + if (nread == EOF) + { + rc = CDK_File_Error; + break; + } + if (pfx->md) + gcry_md_write (pfx->md, buf, nread); + cdk_stream_write (so, buf, nread); + pt->len -= nread; + if (pfx->blkmode.on) + { + pfx->blkmode.size = _cdk_pkt_read_len (in, &pfx->blkmode.on); + if (pfx->blkmode.size == (size_t) EOF) + return CDK_Inv_Packet; + } + if (pt->len <= 0 && !pfx->blkmode.on) + break; + } + + cdk_stream_close (si); + cdk_stream_close (so); + cdk_pkt_release (pkt); + return rc; +} + + +static char +intmode_to_char (int mode) +{ + switch (mode) + { + case CDK_LITFMT_BINARY: + return 'b'; + case CDK_LITFMT_TEXT: + return 't'; + case CDK_LITFMT_UNICODE: + return 'u'; + default: + return 'b'; + } + + return 'b'; +} + + +static cdk_error_t +literal_encode (void *opaque, FILE * in, FILE * out) +{ + literal_filter_t *pfx = opaque; + cdk_pkt_literal_t pt; + cdk_stream_t si; + cdk_packet_t pkt; + size_t filelen; + cdk_error_t rc; + + _cdk_log_debug ("literal filter: encode\n"); + + if (!pfx || !in || !out) + return CDK_Inv_Value; + if (!pfx->filename) + { + pfx->filename = cdk_strdup ("_CONSOLE"); + if (!pfx->filename) + return CDK_Out_Of_Core; + } + + rc = _cdk_stream_fpopen (in, STREAMCTL_READ, &si); + if (rc) + return rc; + + filelen = strlen (pfx->filename); + cdk_pkt_new (&pkt); + pt = pkt->pkt.literal = cdk_calloc (1, sizeof *pt + filelen - 1); + if (!pt) + { + cdk_pkt_release (pkt); + cdk_stream_close (si); + return CDK_Out_Of_Core; + } + memcpy (pt->name, pfx->filename, filelen); + pt->namelen = filelen; + pt->name[pt->namelen] = '\0'; + pt->timestamp = (u32) time (NULL); + pt->mode = intmode_to_char (pfx->mode); + pt->len = cdk_stream_get_length (si); + pt->buf = si; + pkt->old_ctb = 1; + pkt->pkttype = CDK_PKT_LITERAL; + pkt->pkt.literal = pt; + rc = _cdk_pkt_write_fp (out, pkt); + + cdk_pkt_release (pkt); + cdk_stream_close (si); + return rc; +} + + +int +_cdk_filter_literal (void *opaque, int ctl, FILE * in, FILE * out) +{ + if (ctl == STREAMCTL_READ) + return literal_decode (opaque, in, out); + else if (ctl == STREAMCTL_WRITE) + return literal_encode (opaque, in, out); + else if (ctl == STREAMCTL_FREE) + { + literal_filter_t *pfx = opaque; + if (pfx) + { + _cdk_log_debug ("free literal filter\n"); + cdk_free (pfx->filename); + pfx->filename = NULL; + cdk_free (pfx->orig_filename); + pfx->orig_filename = NULL; + return 0; + } + } + return CDK_Inv_Mode; +} + + +static int +text_encode (void *opaque, FILE * in, FILE * out) +{ + const char *s; + char buf[2048]; + + if (!in || !out) + return CDK_Inv_Value; + + /* FIXME: This code does not work for very long lines. */ + while (!feof (in)) + { + s = fgets (buf, DIM (buf) - 1, in); + if (!s) + break; + _cdk_trim_string (buf, 1); + fwrite (buf, 1, strlen (buf), out); + } + + return 0; +} + + +static int +text_decode (void *opaque, FILE * in, FILE * out) +{ + text_filter_t *tfx = opaque; + const char *s; + char buf[2048]; + + if (!tfx || !in || !out) + return CDK_Inv_Value; + + while (!feof (in)) + { + s = fgets (buf, DIM (buf) - 1, in); + if (!s) + break; + _cdk_trim_string (buf, 0); + fwrite (buf, 1, strlen (buf), out); + fwrite (tfx->lf, 1, strlen (tfx->lf), out); + } + + return 0; +} + + +int +_cdk_filter_text (void *opaque, int ctl, FILE * in, FILE * out) +{ + if (ctl == STREAMCTL_READ) + return text_encode (opaque, in, out); + else if (ctl == STREAMCTL_WRITE) + return text_decode (opaque, in, out); + else if (ctl == STREAMCTL_FREE) + { + text_filter_t *tfx = opaque; + if (tfx) + { + _cdk_log_debug ("free text filter\n"); + tfx->lf = NULL; + } + } + return CDK_Inv_Mode; +} diff --git a/src/daemon/https/opencdk/main.c b/src/daemon/https/opencdk/main.c new file mode 100644 index 00000000..be5aec62 --- /dev/null +++ b/src/daemon/https/opencdk/main.c @@ -0,0 +1,779 @@ +/* main.c + * Copyright (C) 2001, 2002, 2003, 2007 Timo Schulz + * + * This file is part of OpenCDK. + * + * OpenCDK 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. + * + * OpenCDK 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. + */ +#ifdef HAVE_CONFIG_H +# include +#endif +#include +#include +#ifdef HAVE_UNISTD_H +# include +#endif +#ifdef _WIN32 +#include +#endif + +#include "opencdk.h" +#include "main.h" +#include "packet.h" + + +/* Set a default cipher algorithm and a digest algorithm. + Even if AES and SHA-256 are not 'MUST' in the latest + OpenPGP draft, AES seems to be a good choice. */ +#define DEFAULT_CIPHER_ALGO GCRY_CIPHER_AES +#define DEFAULT_DIGEST_ALGO GCRY_MD_SHA256 + +/* The site of the secure memory which is allocated in gcrypt. */ +#define SECMEM_SIZE 16384 + + +/* Hooks to custom memory allocation functions. */ +static void *(*alloc_func) (size_t n) = gcry_xmalloc; +static void *(*alloc_secure_func) (size_t n) = gcry_malloc_secure; +static void *(*realloc_func) (void *p, size_t n) = gcry_realloc; +static void *(*calloc_func) (size_t m, size_t n) = gcry_calloc; +static void (*free_func) (void *) = gcry_free; +static int malloc_hooks = 0; +static int secmem_init = 0; + +/* Global settings for the logging. */ +static cdk_log_fnc_t log_handler = NULL; +static void *log_handler_value = NULL; +static int log_level = CDK_LOG_NONE; + + +/** + * cdk_strerror: + * @ec: the error number + * + * Return an error text for the given id. + **/ +const char * +cdk_strerror (int ec) +{ + static char buf[20]; + + switch (ec) + { + case CDK_EOF: + return "End Of File"; + case CDK_Success: + return "No error"; + case CDK_General_Error: + return "General error"; + case CDK_File_Error: + return strerror (errno); + case CDK_Bad_Sig: + return "Bad signature"; + case CDK_Inv_Packet: + return "Invalid packet"; + case CDK_Inv_Algo: + return "Invalid algorithm"; + case CDK_Not_Implemented: + return "This is not implemented yet"; + case CDK_Armor_Error: + return "ASCII armor error"; + case CDK_Armor_CRC_Error: + return "ASCII armored damaged (CRC error)"; + case CDK_MPI_Error: + return "Invalid or missformed MPI"; + case CDK_Inv_Value: + return "Invalid parameter or value"; + case CDK_Error_No_Key: + return "No key available or not found"; + case CDK_Chksum_Error: + return "Check for key does not match"; + case CDK_Time_Conflict: + return "Time conflict"; + case CDK_Zlib_Error: + return "ZLIB error"; + case CDK_Weak_Key: + return "Weak key was detected"; + case CDK_Out_Of_Core: + return "Out of core!!"; + case CDK_Wrong_Seckey: + return "Wrong secret key"; + case CDK_Wrong_Format: + return "Data has wrong format"; + case CDK_Bad_MDC: + return "Manipulated MDC detected"; + case CDK_Inv_Mode: + return "Invalid mode"; + case CDK_Error_No_Keyring: + return "No keyring available"; + case CDK_Inv_Packet_Ver: + return "Invalid version for packet"; + case CDK_Too_Short: + return "Buffer or object is too short"; + case CDK_Unusable_Key: + return "Unusable public key"; + case CDK_No_Data: + return "No data"; + case CDK_No_Passphrase: + return "No passphrase supplied"; + case CDK_Network_Error: + return "A network error occurred"; + default: + sprintf (buf, "ec=%d", ec); + return buf; + } + return NULL; +} + + +static void +out_of_core (size_t n) +{ + fprintf (stderr, "\n ** fatal error: out of memory (%d bytes) **\n", n); +} + + +/** + * cdk_set_malloc_hooks: + * @new_alloc_func: malloc replacement + * @new_alloc_secure_func: secure malloc replacement + * @new_realloc_func: realloc replacement + * @new_calloc_func: calloc replacement + * @new_free_func: free replacement + * + * Set private memory hooks for the library. + */ +void +cdk_set_malloc_hooks (void *(*new_alloc_func) (size_t n), + void *(*new_alloc_secure_func) (size_t n), + void *(*new_realloc_func) (void *p, size_t n), + void *(*new_calloc_func) (size_t m, size_t n), + void (*new_free_func) (void *)) +{ + alloc_func = new_alloc_func; + alloc_secure_func = new_alloc_secure_func; + realloc_func = new_realloc_func; + calloc_func = new_calloc_func; + free_func = new_free_func; + malloc_hooks = 1; +} + + +/** + * cdk_malloc_hook_initialized: + * + * Return if the malloc hooks are already initialized. + **/ +int +cdk_malloc_hook_initialized (void) +{ + return malloc_hooks; +} + + +void * +cdk_malloc (size_t size) +{ + void *p = alloc_func (size); + if (!p) + out_of_core (size); + return p; +} + + +/** + * cdk_calloc: + * @n: amount of elements + * @m: size of one element + * + * Safe wrapper around the c-function calloc. + **/ +void * +cdk_calloc (size_t n, size_t m) +{ + void *p = calloc_func (n, m); + if (!p) + out_of_core (m); + return p; +} + + +/* Things which need to be done after the secure memory initialisation. */ +static void +_secmem_finish (void) +{ + gcry_control (GCRYCTL_DROP_PRIVS); +} + + +/* Initialize the secure memory. */ +static void +_secmem_init (size_t size) +{ + if (secmem_init == 1) + return; + if (size >= SECMEM_SIZE) + size = SECMEM_SIZE; + + /* Check if no other library has already initialized gcrypt. */ + if (!gcry_control (GCRYCTL_ANY_INITIALIZATION_P)) + { + _cdk_log_debug ("init: libgcrypt initialize.\n"); + gcry_control (GCRYCTL_INIT_SECMEM, size, 0); + gcry_control (GCRYCTL_USE_SECURE_RNDPOOL); + gcry_control (GCRYCTL_DISABLE_SECMEM_WARN); + gcry_control (GCRYCTL_INITIALIZATION_FINISHED, NULL, 0); + secmem_init = 1; + } +} + + +/* Things which needs to be done to deinit the secure memory. */ +static void +_secmem_end (void) +{ + gcry_control (GCRYCTL_TERM_SECMEM); + secmem_init = 0; +} + + +/* The Windows system needs to startup the Winsock interface first + before we can use any socket related function. */ +#ifdef _WIN32 +static void +init_sockets (void) +{ + static int initialized = 0; + WSADATA wsdata; + + if (initialized) + return; + if (WSAStartup (0x202, &wsdata)) + _cdk_log_debug ("winsock init failed.\n"); + + initialized = 1; +} + +static void +deinit_sockets (void) +{ + WSACleanup (); +} +#else +void +init_sockets (void) +{ +} +void +deinit_sockets (void) +{ +} +#endif + + +/** + * cdk_lib_startup: + * + * Prepare the internal structures of the library. + * This function should be called before any other CDK function. + */ +void +cdk_lib_startup (void) +{ + _secmem_init (SECMEM_SIZE); + _secmem_finish (); + init_sockets (); +} + + +/** + * cdk_lib_shutdown: + * + * Shutdown the library and free all internal and globally used + * memory and structures. This function should be called in the + * exit handler of the calling program. + */ +void +cdk_lib_shutdown (void) +{ + deinit_sockets (); + _secmem_end (); +} + +/** + * cdk_salloc: + * @size: how much bytes should be allocated. + * @clear: shall the buffer cleared after the allocation? + * + * Allocated the requested amount of bytes in 'secure' memory. + */ +void * +cdk_salloc (size_t size, int clear) +{ + void *p; + + if (!secmem_init) + _secmem_init (SECMEM_SIZE); + + p = alloc_secure_func (size); + if (!p) + out_of_core (size); + if (clear) + memset (p, 0, size); + return p; +} + + +void * +cdk_realloc (void *ptr, size_t size) +{ + void *p = realloc_func (ptr, size); + if (!p) + out_of_core (size); + return p; +} + + +char * +cdk_strdup (const char *ptr) +{ + char *p = cdk_malloc (strlen (ptr) + 1); + if (p) + strcpy (p, ptr); + return p; +} + + +void +cdk_free (void *ptr) +{ + if (ptr) + free_func (ptr); +} + + +/* Internal logging routine. */ +static void +_cdk_logv (int level, const char *fmt, va_list arg_ptr) +{ + + if (log_handler) + log_handler (log_handler_value, level, fmt, arg_ptr); + else + { + if (level == CDK_LOG_NONE) + return; + if (level == CDK_LOG_DEBUG) + fputs ("DBG: ", stderr); + vfprintf (stderr, fmt, arg_ptr); + } +} + + +/** + * cdk_set_log_handler: + * @logfnc: the function pointer + * @opaque: a private values for the function + * + * Set a custom handler for logging. + **/ +void +cdk_set_log_handler (cdk_log_fnc_t logfnc, void *opaque) +{ + log_handler = logfnc; + log_handler_value = opaque; +} + + +/** + * cdk_set_log_level: + * @lvl: the level + * + * Set the verbosity level. + **/ +void +cdk_set_log_level (int level) +{ + log_level = level; +} + + +/* Return the current log level of the lib. */ +int +_cdk_get_log_level (void) +{ + return log_level; +} + + +void +_cdk_log_info (const char *fmt, ...) +{ + va_list arg; + + if (log_level == CDK_LOG_NONE) + return; + va_start (arg, fmt); + _cdk_logv (CDK_LOG_INFO, fmt, arg); + va_end (arg); +} + + +void +_cdk_log_debug (const char *fmt, ...) +{ + va_list arg; + + if (log_level < CDK_LOG_DEBUG) + return; + va_start (arg, fmt); + _cdk_logv (CDK_LOG_DEBUG, fmt, arg); + va_end (arg); +} + + +/* Use the passphrase callback in the handle HD or + return NULL if there is no valid callback. */ +char * +_cdk_passphrase_get (cdk_ctx_t hd, const char *prompt) +{ + if (!hd || !hd->passphrase_cb) + return NULL; + return hd->passphrase_cb (hd->passphrase_cb_value, prompt); +} + + +static void +handle_set_cipher (cdk_ctx_t hd, int cipher) +{ + if (!hd) + return; + if (gcry_cipher_test_algo (cipher)) + cipher = DEFAULT_CIPHER_ALGO; + hd->cipher_algo = cipher; +} + + +static void +handle_set_digest (cdk_ctx_t hd, int digest) +{ + if (!hd) + return; + if (gcry_md_test_algo (digest)) + digest = DEFAULT_DIGEST_ALGO; + hd->digest_algo = digest; +} + + +static void +handle_set_s2k (cdk_ctx_t hd, int mode, int digest, int cipher) +{ + if (!hd) + return; + if (gcry_cipher_test_algo (cipher)) + cipher = DEFAULT_CIPHER_ALGO; + if (gcry_md_test_algo (digest)) + digest = DEFAULT_DIGEST_ALGO; + if (mode != CDK_S2K_SIMPLE && + mode != CDK_S2K_SALTED && mode != CDK_S2K_ITERSALTED) + mode = CDK_S2K_ITERSALTED; + hd->_s2k.mode = mode; + hd->_s2k.digest_algo = digest; +} + + +static void +handle_set_compress (cdk_ctx_t hd, int algo, int level) +{ + if (!hd) + return; + if (algo < 0 || algo > 2) + algo = 0; + hd->compress.algo = algo; + if (!algo) + hd->opt.compress = 0; + else + { + if (level > 0 && level < 10) + hd->compress.level = level; + else + hd->compress.level = 6; + } +} + + +/** + * cdk_handle_control: + * @hd: session handle + * @action: flag which indicates whether put or get is requested + * @cmd: command id + * + * Perform various control operations for the current session. + **/ +int +cdk_handle_control (cdk_ctx_t hd, int action, int cmd, ...) +{ + va_list arg_ptr; + int set = action == CDK_CTLF_SET, val = 0; + + if (!hd) + return -1; + + if (action != CDK_CTLF_SET && action != CDK_CTLF_GET) + return -1; + va_start (arg_ptr, cmd); + switch (cmd) + { + case CDK_CTL_ARMOR: + if (set) + hd->opt.armor = va_arg (arg_ptr, int); + else + val = hd->opt.armor; + break; + + case CDK_CTL_CIPHER: + if (set) + handle_set_cipher (hd, va_arg (arg_ptr, int)); + else + val = hd->cipher_algo; + break; + + case CDK_CTL_DIGEST: + if (set) + handle_set_digest (hd, va_arg (arg_ptr, int)); + else + val = hd->digest_algo; + break; + + case CDK_CTL_OVERWRITE: + if (set) + hd->opt.overwrite = va_arg (arg_ptr, int); + else + val = hd->opt.overwrite; + break; + + case CDK_CTL_COMPRESS: + if (set) + { + int algo = va_arg (arg_ptr, int); + int level = va_arg (arg_ptr, int); + handle_set_compress (hd, algo, level); + } + else + val = hd->compress.algo; + break; + + case CDK_CTL_S2K: + if (set) + { + int mode = va_arg (arg_ptr, int); + int digest = va_arg (arg_ptr, int); + int cipher = va_arg (arg_ptr, int); + handle_set_s2k (hd, mode, digest, cipher); + } + else + val = hd->_s2k.mode; + break; + + case CDK_CTL_FORCE_DIGEST: + if (set) + hd->opt.force_digest = va_arg (arg_ptr, int); + else + val = hd->opt.force_digest; + break; + + case CDK_CTL_BLOCKMODE_ON: + if (set) + hd->opt.blockmode = va_arg (arg_ptr, int); + else + val = hd->opt.blockmode; + break; + + default: + val = -1; + break; + } + va_end (arg_ptr); + return val; +} + + + +/** + * cdk_handle_new: + * @r_ctx: context to store the handle + * + * create a new session handle. + **/ +cdk_error_t +cdk_handle_new (cdk_ctx_t * r_ctx) +{ + cdk_ctx_t c; + + if (!r_ctx) + return CDK_Inv_Value; + + c = cdk_calloc (1, sizeof *c); + if (!c) + return CDK_Out_Of_Core; + + /* For S2K use the iterated and salted mode and use the + default digest and cipher algorithms. Because the MDC + feature will be used, the default cipher should use a + blocksize of 128 bits. */ + c->_s2k.mode = CDK_S2K_ITERSALTED; + c->_s2k.digest_algo = DEFAULT_DIGEST_ALGO; + + c->opt.mdc = 1; + c->opt.compress = 1; + c->opt.armor = 0; + c->opt.textmode = 0; + + c->digest_algo = DEFAULT_DIGEST_ALGO; + c->cipher_algo = DEFAULT_CIPHER_ALGO; + + c->compress.algo = CDK_COMPRESS_ZIP; + c->compress.level = 6; + + *r_ctx = c; + return 0; +} + + +/** + * cdk_handle_set_keyring: + * @hd: session handle + * @type: public=0 or secret=1 keyring type + * @kringname: file name of the keyring which shall be used. + * + * Convenient function to set the keyring for the current session. + */ +cdk_error_t +cdk_handle_set_keyring (cdk_ctx_t hd, int type, const char *kringname) +{ + cdk_keydb_hd_t db; + cdk_error_t err; + + err = cdk_keydb_new_from_file (&db, type, kringname); + if (err) + return err; + + if (!type) + hd->db.pub = db; + else + hd->db.sec = db; + hd->db.close_db = 1; + return 0; +} + + +/** + * cdk_handle_set_keydb: + * @hd: session handle + * @db: the database handle + * + * set the key database handle. + * the function automatically detects whether this is a public or + * secret keyring and the right handle is set. + **/ +void +cdk_handle_set_keydb (cdk_ctx_t hd, cdk_keydb_hd_t db) +{ + if (!hd) + return; + if (_cdk_keydb_is_secret (db)) + hd->db.sec = db; + else + hd->db.pub = db; +} + + +/** + * cdk_handle_get_keydb: + * @hd: session handle + * @type: type of the keyring + * + * Return the keydb handle from the session handle. + * The caller should not free these handles. + **/ +cdk_keydb_hd_t +cdk_handle_get_keydb (cdk_ctx_t hd, int type) +{ + if (!hd) + return NULL; + if (type == CDK_DBTYPE_PK_KEYRING) + return hd->db.pub; + else if (type == CDK_DBTYPE_SK_KEYRING) + return hd->db.sec; + return NULL; +} + + +/** + * cdk_handle_set_passphrase_cb: + * @hd: session handle + * @cb: callback function + * @cb_value: the opaque value for the cb function + * + * set the passphrase callback. + **/ +void +cdk_handle_set_passphrase_cb (cdk_ctx_t hd, + char *(*cb) (void *opa, const char *prompt), + void *cb_value) +{ + if (!hd) + return; + hd->passphrase_cb = cb; + hd->passphrase_cb_value = cb_value; +} + + +/** + * cdk_handle_verify_get_result: + * @hd: the session handle + * + * Return the verify result for the current session. + * Do not free the pointer. + **/ +cdk_verify_result_t +cdk_handle_verify_get_result (cdk_ctx_t hd) +{ + return hd->result.verify; +} + + +/** + * cdk_handle_free: + * @hd: the handle + * + * Release the main handle. + **/ +void +cdk_handle_free (cdk_ctx_t hd) +{ + if (!hd) + return; + _cdk_result_verify_free (hd->result.verify); + + /* If cdk_handle_set_keyring() were used, we need to free the key db + handles here because the handles are not controlled by the user. */ + if (hd->db.close_db) + { + if (hd->db.pub) + cdk_keydb_free (hd->db.pub); + if (hd->db.sec) + cdk_keydb_free (hd->db.sec); + hd->db.pub = hd->db.sec = NULL; + } + cdk_free (hd->dek); + cdk_free (hd); +} diff --git a/src/daemon/https/opencdk/main.h b/src/daemon/https/opencdk/main.h new file mode 100644 index 00000000..3e9a443d --- /dev/null +++ b/src/daemon/https/opencdk/main.h @@ -0,0 +1,183 @@ +/* main.h + * Copyright (C) 2002, 2003, 2007 Timo Schulz + * + * This file is part of OpenCDK. + * + * OpenCDK 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. + * + * OpenCDK 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. + */ +#ifndef CDK_MAIN_H +#define CDK_MAIN_H + +#include +#include "types.h" + +/* The general size of a buffer for the variou modules. */ +#define BUFSIZE 8192 + +/* This is the default block size for the partial length packet mode. */ +#define DEF_BLOCKSIZE 8192 +#define DEF_BLOCKBITS 13 /* 2^13 = 8192 */ + +/* For now SHA-1 is used to create fingerprint for keys. + But if this will ever change, it is a good idea to + have a constant for it to avoid to change it in all files. */ +#define KEY_FPR_LEN 20 + +#include "context.h" + +/* The maximal amount of bits a multi precsion integer can have. */ +#define MAX_MPI_BITS 16384 +#define MAX_MPI_BYTES (MAX_MPI_BITS/8) + + +/* Because newer DSA variants are not limited to SHA-1, we must consider + that SHA-512 is used and increase the buffer size of the digest. */ +#define MAX_DIGEST_LEN 64 + +/* Helper to find out if the signature were made over a user ID + or if the signature revokes a previous user ID. */ +#define IS_UID_SIG(s) (((s)->sig_class & ~3) == 0x10) +#define IS_UID_REV(s) ((s)->sig_class == 0x30) + +#define DEBUG_PKT (_cdk_get_log_level () == (CDK_LOG_DEBUG+1)) + +/* Helper to find out if a key has the requested capability. */ +#define KEY_CAN_ENCRYPT(a) (_cdk_pk_algo_usage ((a)) & CDK_KEY_USG_ENCR) +#define KEY_CAN_SIGN(a) (_cdk_pk_algo_usage ((a)) & CDK_KEY_USG_SIGN) +#define KEY_CAN_AUTH(a) (_cdk_pk_algo_usage ((a)) & CDK_KEY_USG_AUTH) + +/* Helper macro to make sure the buffer is overwritten. */ +#define wipemem(_ptr,_len) do { \ + volatile char *_vptr = (volatile char *)(_ptr); \ + size_t _vlen = (_len); \ + while (_vlen) \ + { \ + *_vptr = 0; \ + _vptr++; \ + _vlen--; \ + } } while (0) + +/*-- armor.c --*/ +const char * _cdk_armor_get_lineend (void); + +/*-- main.c --*/ +int _cdk_get_log_level (void); +void _cdk_log_info (const char * fmt, ...); +void _cdk_log_debug (const char * fmt, ...); +char * _cdk_passphrase_get (cdk_ctx_t hd, const char *prompt); + +/*-- misc.c --*/ +int _cdk_check_args( int overwrite, const char * in, const char * out ); +u32 _cdk_buftou32 (const byte * buf); +void _cdk_u32tobuf (u32 u, byte * buf); +const char *_cdk_memistr (const char * buf, size_t buflen, const char * sub); +cdk_error_t _cdk_map_gcry_error (gcry_error_t err); +#define map_gcry_error(err) _cdk_map_gcry_error (err) + +/* Helper to provide case insentensive strstr version. */ +#define stristr(haystack, needle) \ + _cdk_memistr((haystack), strlen (haystack), (needle)) + +/*-- proc-packet.c --*/ +cdk_error_t _cdk_proc_packets (cdk_ctx_t hd, cdk_stream_t inp, + cdk_stream_t data, + const char *output, cdk_stream_t outstream, + gcry_md_hd_t md); +cdk_error_t _cdk_pkt_write2 (cdk_stream_t out, int pkttype, void *pktctx); + +/*-- pubkey.c --*/ +u32 _cdk_pkt_get_keyid (cdk_packet_t pkt, u32 * keyid); +cdk_error_t _cdk_pkt_get_fingerprint (cdk_packet_t pkt, byte *fpr); +int _cdk_pk_algo_usage (int algo); +int _cdk_pk_test_algo (int algo, unsigned int usage); +int _cdk_sk_get_csum (cdk_pkt_seckey_t sk); + +/*-- new-packet.c --*/ +byte * _cdk_subpkt_get_array (cdk_subpkt_t s, int count, size_t * r_nbytes); +cdk_error_t _cdk_subpkt_copy (cdk_subpkt_t * r_dst, cdk_subpkt_t src); +void _cdk_pkt_detach_free (cdk_packet_t pkt, int *r_pkttype, void **ctx); + +/*-- sig-check.c --*/ +cdk_error_t _cdk_sig_check (cdk_pkt_pubkey_t pk, cdk_pkt_signature_t sig, + gcry_md_hd_t digest, int * r_expired); +cdk_error_t _cdk_hash_sig_data (cdk_pkt_signature_t sig, gcry_md_hd_t hd); +cdk_error_t _cdk_hash_userid (cdk_pkt_userid_t uid, int sig_version, gcry_md_hd_t md); +cdk_error_t _cdk_hash_pubkey (cdk_pkt_pubkey_t pk, gcry_md_hd_t md, + int use_fpr); +cdk_error_t _cdk_pk_check_sig (cdk_keydb_hd_t hd, + cdk_kbnode_t knode, + cdk_kbnode_t snode, int *is_selfsig); + +/*-- kbnode.c --*/ +void _cdk_kbnode_add (cdk_kbnode_t root, cdk_kbnode_t node); +void _cdk_kbnode_clone (cdk_kbnode_t node); + +/*-- sesskey.c --*/ +cdk_error_t _cdk_digest_encode_pkcs1 (byte **r_md, size_t *r_mdlen, + int pk_algo, + const byte * md, + int digest_algo, unsigned nbits); +cdk_error_t _cdk_sk_unprotect_auto (cdk_ctx_t hd, cdk_pkt_seckey_t sk); + +/*-- keydb.c --*/ +int _cdk_keydb_is_secret (cdk_keydb_hd_t db); +cdk_error_t _cdk_keydb_get_pk_byusage (cdk_keydb_hd_t hd, const char * name, + cdk_pkt_pubkey_t * ret_pk, int usage); +cdk_error_t _cdk_keydb_get_sk_byusage (cdk_keydb_hd_t hd, const char * name, + cdk_pkt_seckey_t * ret_sk, int usage); +cdk_error_t _cdk_keydb_check_userid (cdk_keydb_hd_t hd, u32 * keyid, + const char * id); + +/*-- sign.c --*/ +int _cdk_sig_hash_for (cdk_pkt_pubkey_t pk); +void _cdk_trim_string (char * s, int canon); +cdk_error_t _cdk_sig_create (cdk_pkt_pubkey_t pk, cdk_pkt_signature_t sig); +cdk_error_t _cdk_sig_complete (cdk_pkt_signature_t sig, cdk_pkt_seckey_t sk, + gcry_md_hd_t hd); + +/*-- stream.c --*/ +void _cdk_stream_set_compress_algo (cdk_stream_t s, int algo); +cdk_error_t _cdk_stream_open_mode (const char *file, const char *mode, + cdk_stream_t *ret_s); +void * _cdk_stream_get_opaque( cdk_stream_t s, int fid ); +const char * _cdk_stream_get_fname( cdk_stream_t s ); +FILE * _cdk_stream_get_fp( cdk_stream_t s ); +int _cdk_stream_gets( cdk_stream_t s, char * buf, size_t count ); +cdk_error_t _cdk_stream_append( const char * file, cdk_stream_t * ret_s ); +int _cdk_stream_get_errno( cdk_stream_t s ); +cdk_error_t _cdk_stream_set_blockmode( cdk_stream_t s, size_t nbytes ); +int _cdk_stream_get_blockmode( cdk_stream_t s ); +int _cdk_stream_puts( cdk_stream_t s, const char * buf ); +cdk_error_t _cdk_stream_fpopen (FILE * fp, unsigned write_mode, + cdk_stream_t *ret_out); + +/*-- verify.c --*/ +void _cdk_result_verify_free (cdk_verify_result_t res); +cdk_verify_result_t _cdk_result_verify_new (void); + + +/*-- read-packet.c --*/ +size_t _cdk_pkt_read_len (FILE * inp, size_t *ret_partial); + +/*-- write-packet.c --*/ +cdk_error_t _cdk_pkt_write_fp( FILE * out, cdk_packet_t pkt ); + +/*-- seskey.c --*/ +cdk_error_t _cdk_s2k_copy (cdk_s2k_t *r_dst, cdk_s2k_t src); + +cdk_error_t cdk_dek_encode_pkcs1 (cdk_dek_t dek, size_t nbits, + gcry_mpi_t *r_enc); +cdk_error_t cdk_dek_decode_pkcs1 (cdk_dek_t * ret_dek, gcry_mpi_t esk); +cdk_error_t cdk_dek_extract (cdk_dek_t * ret_dek, cdk_ctx_t hd, + cdk_pkt_pubkey_enc_t enc, + cdk_pkt_seckey_t sk ); + +#endif /* CDK_MAIN_H */ diff --git a/src/daemon/https/opencdk/misc.c b/src/daemon/https/opencdk/misc.c new file mode 100644 index 00000000..c296de40 --- /dev/null +++ b/src/daemon/https/opencdk/misc.c @@ -0,0 +1,571 @@ +/* misc.c + * Copyright (C) 2002, 2003 Timo Schulz + * Copyright (C) 1998-2002, 2007 Free Software Foundation, Inc. + * + * This file is part of OpenCDK. + * + * OpenCDK 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. + * + * OpenCDK 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. + */ +#ifdef HAVE_CONFIG_H +# include +#endif +#include +#include +#include +#include + +#include "opencdk.h" +#include "main.h" + + +u32 +_cdk_buftou32 (const byte * buf) +{ + u32 u; + + if (!buf) + return 0; + u = buf[0] << 24; + u |= buf[1] << 16; + u |= buf[2] << 8; + u |= buf[3]; + return u; +} + + +void +_cdk_u32tobuf (u32 u, byte * buf) +{ + if (!buf) + return; + buf[0] = u >> 24; + buf[1] = u >> 16; + buf[2] = u >> 8; + buf[3] = u; +} + + +static const char * +parse_version_number (const char *s, int *number) +{ + int val = 0; + + if (*s == '0' && isdigit (s[1])) + return NULL; + /* leading zeros are not allowed */ + for (; isdigit (*s); s++) + { + val *= 10; + val += *s - '0'; + } + *number = val; + return val < 0 ? NULL : s; +} + + +static const char * +parse_version_string (const char *s, int *major, int *minor, int *micro) +{ + s = parse_version_number (s, major); + if (!s || *s != '.') + return NULL; + s++; + s = parse_version_number (s, minor); + if (!s || *s != '.') + return NULL; + s++; + s = parse_version_number (s, micro); + if (!s) + return NULL; + return s; /* patchlevel */ +} + + +/** + * cdk_check_version: + * @req_version: The requested version + * + * Check that the the version of the library is at minimum the requested + * one and return the version string; return NULL if the condition is + * not satisfied. If a NULL is passed to this function, no check is done, + *but the version string is simply returned. + **/ +const char * +cdk_check_version (const char *req_version) +{ + const char *ver = VERSION; + int my_major, my_minor, my_micro; + int rq_major, rq_minor, rq_micro; + const char *my_plvl, *rq_plvl; + + if (!req_version) + return ver; + my_plvl = parse_version_string (ver, &my_major, &my_minor, &my_micro); + if (!my_plvl) + return NULL; + /* very strange our own version is bogus */ + rq_plvl = parse_version_string (req_version, &rq_major, &rq_minor, + &rq_micro); + if (!rq_plvl) + return NULL; /* req version string is invalid */ + if (my_major > rq_major + || (my_major == rq_major && my_minor > rq_minor) + || (my_major == rq_major && my_minor == rq_minor + && my_micro > rq_micro) + || (my_major == rq_major && my_minor == rq_minor + && my_micro == rq_micro && strcmp (my_plvl, rq_plvl) >= 0)) + return ver; + return NULL; +} + + +/** + * cdk_strlist_free: + * @sl: the string list + * + * Release the string list object. + **/ +void +cdk_strlist_free (cdk_strlist_t sl) +{ + cdk_strlist_t sl2; + + for (; sl; sl = sl2) + { + sl2 = sl->next; + cdk_free (sl); + } +} + + +/** + * cdk_strlist_add: + * @list: destination string list + * @string: the string to add + * + * Add the given list to the string list. + **/ +cdk_strlist_t +cdk_strlist_add (cdk_strlist_t * list, const char *string) +{ + cdk_strlist_t sl; + + if (!string) + return NULL; + + sl = cdk_calloc (1, sizeof *sl + strlen (string) + 1); + if (!sl) + return NULL; + strcpy (sl->d, string); + sl->next = *list; + *list = sl; + return sl; +} + + +/** + * cdk_strlist_next: + * @root: the opaque string list. + * @r_str: optional argument to store the string data. + * + * Return the next string list node from @root. The optional + * argument @r_str return the data of the current (!) node. + **/ +cdk_strlist_t +cdk_strlist_next (cdk_strlist_t root, const char **r_str) +{ + cdk_strlist_t node; + + if (root && r_str) + *r_str = root->d; + for (node = root->next; node; node = node->next) + return node; + + return NULL; +} + + +const char * +_cdk_memistr (const char *buf, size_t buflen, const char *sub) +{ + const byte *t, *s; + size_t n; + + for (t = (byte *) buf, n = buflen, s = (byte *) sub; n; t++, n--) + { + if (toupper (*t) == toupper (*s)) + { + for (buf = t++, buflen = n--, s++; + n && toupper (*t) == toupper ((byte) * s); t++, s++, n--) + ; + if (!*s) + return buf; + t = (byte *) buf; + n = buflen; + s = (byte *) sub; + } + } + + return NULL; +} + + +/** + * cdk_utf8_encode: + * @string: + * + * Encode the given string in utf8 and return it. + **/ +char * +cdk_utf8_encode (const char *string) +{ + const byte *s; + char *buffer; + byte *p; + size_t length; + + /* FIXME: We should use iconv if possible for utf8 issues. */ + for (s = (const byte *) string, length = 0; *s; s++) + { + length++; + if (*s & 0x80) + length++; + } + + buffer = cdk_calloc (1, length + 1); + for (p = (byte *) buffer, s = (byte *) string; *s; s++) + { + if (*s & 0x80) + { + *p++ = 0xc0 | ((*s >> 6) & 3); + *p++ = 0x80 | (*s & 0x3f); + } + else + *p++ = *s; + } + *p = 0; + return buffer; +} + + +/** + * cdk_utf8_decode: + * @string: the string to decode + * @length: the length of the string + * @delim: the delimiter + * + * Decode the given utf8 string and return the native representation. + **/ +char * +cdk_utf8_decode (const char *string, size_t length, int delim) +{ + int nleft; + int i; + byte encbuf[8]; + int encidx; + const byte *s; + size_t n; + byte *buffer = NULL, *p = NULL; + unsigned long val = 0; + size_t slen; + int resync = 0; + + /* 1. pass (p==NULL): count the extended utf-8 characters */ + /* 2. pass (p!=NULL): create string */ + for (;;) + { + for (slen = length, nleft = encidx = 0, n = 0, s = (byte *) string; + slen; s++, slen--) + { + if (resync) + { + if (!(*s < 128 || (*s >= 0xc0 && *s <= 0xfd))) + { + /* still invalid */ + if (p) + { + sprintf ((char *) p, "\\x%02x", *s); + p += 4; + } + n += 4; + continue; + } + resync = 0; + } + if (!nleft) + { + if (!(*s & 0x80)) + { /* plain ascii */ + if (*s < 0x20 || *s == 0x7f || *s == delim || + (delim && *s == '\\')) + { + n++; + if (p) + *p++ = '\\'; + switch (*s) + { + case '\n': + n++; + if (p) + *p++ = 'n'; + break; + case '\r': + n++; + if (p) + *p++ = 'r'; + break; + case '\f': + n++; + if (p) + *p++ = 'f'; + break; + case '\v': + n++; + if (p) + *p++ = 'v'; + break; + case '\b': + n++; + if (p) + *p++ = 'b'; + break; + case 0: + n++; + if (p) + *p++ = '0'; + break; + default: + n += 3; + if (p) + { + sprintf ((char *) p, "x%02x", *s); + p += 3; + } + break; + } + } + else + { + if (p) + *p++ = *s; + n++; + } + } + else if ((*s & 0xe0) == 0xc0) + { /* 110x xxxx */ + val = *s & 0x1f; + nleft = 1; + encidx = 0; + encbuf[encidx++] = *s; + } + else if ((*s & 0xf0) == 0xe0) + { /* 1110 xxxx */ + val = *s & 0x0f; + nleft = 2; + encidx = 0; + encbuf[encidx++] = *s; + } + else if ((*s & 0xf8) == 0xf0) + { /* 1111 0xxx */ + val = *s & 0x07; + nleft = 3; + encidx = 0; + encbuf[encidx++] = *s; + } + else if ((*s & 0xfc) == 0xf8) + { /* 1111 10xx */ + val = *s & 0x03; + nleft = 4; + encidx = 0; + encbuf[encidx++] = *s; + } + else if ((*s & 0xfe) == 0xfc) + { /* 1111 110x */ + val = *s & 0x01; + nleft = 5; + encidx = 0; + encbuf[encidx++] = *s; + } + else + { /* invalid encoding: print as \xnn */ + if (p) + { + sprintf ((char *) p, "\\x%02x", *s); + p += 4; + } + n += 4; + resync = 1; + } + } + else if (*s < 0x80 || *s >= 0xc0) + { /* invalid */ + if (p) + { + for (i = 0; i < encidx; i++) + { + sprintf ((char *) p, "\\x%02x", encbuf[i]); + p += 4; + } + sprintf ((char *) p, "\\x%02x", *s); + p += 4; + } + n += 4 + 4 * encidx; + nleft = 0; + encidx = 0; + resync = 1; + } + else + { + encbuf[encidx++] = *s; + val <<= 6; + val |= *s & 0x3f; + if (!--nleft) + { /* ready native set */ + if (val >= 0x80 && val < 256) + { + n++; /* we can simply print this character */ + if (p) + *p++ = val; + } + else + { /* we do not have a translation: print utf8 */ + if (p) + { + for (i = 0; i < encidx; i++) + { + sprintf ((char *) p, "\\x%02x", encbuf[i]); + p += 4; + } + } + n += encidx * 4; + encidx = 0; + } + } + } + + } + if (!buffer) /* allocate the buffer after the first pass */ + buffer = p = cdk_malloc (n + 1); + else + { + *p = 0; /* make a string */ + return (char *) buffer; + } + } +} + + +/* Map the gcrypt error to a valid opencdk error constant. */ +cdk_error_t +_cdk_map_gcry_error (gcry_error_t err) +{ + /* FIXME: We need to catch them all. */ + switch (gpg_err_code (err)) + { + case GPG_ERR_NO_ERROR: + return CDK_Success; + case GPG_ERR_INV_VALUE: + return CDK_Inv_Value; + case GPG_ERR_GENERAL: + return CDK_General_Error; + case GPG_ERR_INV_PACKET: + return CDK_Inv_Packet; + case GPG_ERR_TOO_SHORT: + return CDK_Too_Short; + case GPG_ERR_TOO_LARGE: + return CDK_Inv_Value; + case GPG_ERR_NO_PUBKEY: + case GPG_ERR_NO_SECKEY: + return CDK_Error_No_Key; + case GPG_ERR_BAD_SIGNATURE: + return CDK_Bad_Sig; + case GPG_ERR_NO_DATA: + return CDK_No_Data; + default: + break; + } + + return (cdk_error_t) err; +} + + +/* Remove all trailing white spaces from the string. */ +void +_cdk_trim_string (char *s, int canon) +{ + while (s && *s && + (s[strlen (s) - 1] == '\t' || + s[strlen (s) - 1] == '\r' || + s[strlen (s) - 1] == '\n' || s[strlen (s) - 1] == ' ')) + s[strlen (s) - 1] = '\0'; + if (canon) + strcat (s, "\r\n"); +} + + +int +_cdk_check_args (int overwrite, const char *in, const char *out) +{ + struct stat stbuf; + + if (!in || !out) + return CDK_Inv_Value; + if (strlen (in) == strlen (out) && strcmp (in, out) == 0) + return CDK_Inv_Mode; + if (!overwrite && !stat (out, &stbuf)) + return CDK_Inv_Mode; + return 0; +} + +#ifdef _WIN32 +#include +#include + +FILE * +my_tmpfile (void) +{ + /* Because the tmpfile() version of wine is not really useful, + we implement our own version to avoid problems with 'make check'. */ + static const char *letters = "abcdefghijklmnopqrstuvwxyz"; + char buf[512], rnd[24]; + FILE *fp; + int fd, i; + + gcry_create_nonce (rnd, DIM (rnd)); + for (i = 0; i < DIM (rnd) - 1; i++) + { + char c = letters[(unsigned char) rnd[i] % 26]; + rnd[i] = c; + } + rnd[DIM (rnd) - 1] = 0; + if (!GetTempPath (464, buf)) + return NULL; + strcat (buf, "_cdk_"); + strcat (buf, rnd); + + /* We need to make sure the file will be deleted when it is closed. */ + fd = _open (buf, _O_CREAT | _O_EXCL | _O_TEMPORARY | + _O_RDWR | _O_BINARY, _S_IREAD | _S_IWRITE); + if (fd == -1) + return NULL; + fp = fdopen (fd, "w+b"); + if (fp != NULL) + return fp; + _close (fd); + return NULL; +} +#else +FILE * +my_tmpfile (void) +{ + return tmpfile (); +} +#endif diff --git a/src/daemon/https/opencdk/new-packet.c b/src/daemon/https/opencdk/new-packet.c new file mode 100644 index 00000000..d6f60690 --- /dev/null +++ b/src/daemon/https/opencdk/new-packet.c @@ -0,0 +1,874 @@ +/* new-packet.c - packet handling (freeing, copying, ...) + * Copyright (C) 2001, 2002, 2003, 2007 Timo Schulz + * + * This file is part of OpenCDK. + * + * OpenCDK 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. + * + * OpenCDK 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. + */ +#ifdef HAVE_CONFIG_H +# include +#endif +#include +#include +#include + +#include "opencdk.h" +#include "main.h" +#include "packet.h" + + +/* Release an array of MPI values. */ +void +_cdk_free_mpibuf (size_t n, gcry_mpi_t * array) +{ + while (n--) + { + gcry_mpi_release (array[n]); + array[n] = NULL; + } +} + + +/** + * cdk_pkt_new: + * @r_pkt: the new packet + * + * Allocate a new packet. + **/ +cdk_error_t +cdk_pkt_new (cdk_packet_t * r_pkt) +{ + cdk_packet_t pkt; + + if (!r_pkt) + return CDK_Inv_Value; + pkt = cdk_calloc (1, sizeof *pkt); + if (!pkt) + return CDK_Out_Of_Core; + *r_pkt = pkt; + return 0; +} + + +static void +free_symkey_enc (cdk_pkt_symkey_enc_t enc) +{ + if (!enc) + return; + cdk_s2k_free (enc->s2k); + cdk_free (enc); +} + + +static void +free_pubkey_enc (cdk_pkt_pubkey_enc_t enc) +{ + size_t nenc; + + if (!enc) + return; + + nenc = cdk_pk_get_nenc (enc->pubkey_algo); + _cdk_free_mpibuf (nenc, enc->mpi); + cdk_free (enc); +} + + +static void +free_literal (cdk_pkt_literal_t pt) +{ + if (!pt) + return; + /* The buffer which is referenced in this packet is closed + elsewhere. To close it here would cause a double close. */ + cdk_free (pt); +} + + +void +_cdk_free_userid (cdk_pkt_userid_t uid) +{ + if (!uid) + return; + + cdk_free (uid->prefs); + uid->prefs = NULL; + cdk_free (uid->attrib_img); + uid->attrib_img = NULL; + cdk_free (uid); +} + + +void +_cdk_free_signature (cdk_pkt_signature_t sig) +{ + cdk_desig_revoker_t r; + size_t nsig; + + if (!sig) + return; + + nsig = cdk_pk_get_nsig (sig->pubkey_algo); + _cdk_free_mpibuf (nsig, sig->mpi); + + cdk_subpkt_free (sig->hashed); + sig->hashed = NULL; + cdk_subpkt_free (sig->unhashed); + sig->unhashed = NULL; + while (sig->revkeys) + { + r = sig->revkeys->next; + cdk_free (sig->revkeys); + sig->revkeys = r; + } + cdk_free (sig); +} + + +void +cdk_pk_release (cdk_pubkey_t pk) +{ + size_t npkey; + + if (!pk) + return; + + npkey = cdk_pk_get_npkey (pk->pubkey_algo); + _cdk_free_userid (pk->uid); + pk->uid = NULL; + cdk_free (pk->prefs); + pk->prefs = NULL; + _cdk_free_mpibuf (npkey, pk->mpi); + cdk_free (pk); +} + + +void +cdk_sk_release (cdk_seckey_t sk) +{ + size_t nskey; + + if (!sk) + return; + + nskey = cdk_pk_get_nskey (sk->pubkey_algo); + _cdk_free_mpibuf (nskey, sk->mpi); + cdk_free (sk->encdata); + sk->encdata = NULL; + cdk_pk_release (sk->pk); + sk->pk = NULL; + cdk_s2k_free (sk->protect.s2k); + sk->protect.s2k = NULL; + cdk_free (sk); +} + + +static void +free_encrypted (cdk_pkt_encrypted_t enc) +{ + if (!enc) + return; + + /* This is just a reference for the filters to know where + the encrypted data starts and to read from the sream. It + us closed elsewhere and to close it here would double close it. */ + /*cdk_stream_close (enc->buf); */ + enc->buf = NULL; + cdk_free (enc); +} + + +/* Detach the openpgp packet from the packet structure + and release the packet structure itself. */ +void +_cdk_pkt_detach_free (cdk_packet_t pkt, int *r_pkttype, void **ctx) +{ + /* For now we just allow this for keys. */ + switch (pkt->pkttype) + { + case CDK_PKT_PUBLIC_KEY: + case CDK_PKT_PUBLIC_SUBKEY: + *ctx = pkt->pkt.public_key; + break; + + case CDK_PKT_SECRET_KEY: + case CDK_PKT_SECRET_SUBKEY: + *ctx = pkt->pkt.secret_key; + break; + + default: + *r_pkttype = 0; + return; + } + + /* The caller might expect a specific packet type and + is not interested to store it for later use. */ + if (r_pkttype) + *r_pkttype = pkt->pkttype; + + cdk_free (pkt); +} + + +void +cdk_pkt_free (cdk_packet_t pkt) +{ + if (!pkt) + return; + + switch (pkt->pkttype) + { + case CDK_PKT_ATTRIBUTE: + case CDK_PKT_USER_ID: + _cdk_free_userid (pkt->pkt.user_id); + break; + case CDK_PKT_PUBLIC_KEY: + case CDK_PKT_PUBLIC_SUBKEY: + cdk_pk_release (pkt->pkt.public_key); + break; + case CDK_PKT_SECRET_KEY: + case CDK_PKT_SECRET_SUBKEY: + cdk_sk_release (pkt->pkt.secret_key); + break; + case CDK_PKT_SIGNATURE: + _cdk_free_signature (pkt->pkt.signature); + break; + case CDK_PKT_PUBKEY_ENC: + free_pubkey_enc (pkt->pkt.pubkey_enc); + break; + case CDK_PKT_SYMKEY_ENC: + free_symkey_enc (pkt->pkt.symkey_enc); + break; + case CDK_PKT_MDC: + cdk_free (pkt->pkt.mdc); + break; + case CDK_PKT_ENCRYPTED: + case CDK_PKT_ENCRYPTED_MDC: + free_encrypted (pkt->pkt.encrypted); + break; + case CDK_PKT_ONEPASS_SIG: + cdk_free (pkt->pkt.onepass_sig); + break; + case CDK_PKT_LITERAL: + free_literal (pkt->pkt.literal); + break; + case CDK_PKT_COMPRESSED: + cdk_free (pkt->pkt.compressed); + break; + default: + break; + } + + /* Reset the packet type to avoid, when cdk_pkt_release() will be + used, that the second cdk_pkt_free() call will double free the data. */ + pkt->pkttype = 0; +} + + +/** + * cdk_pkt_release: + * @pkt: the packet + * + * Free the contents of the given package and + * release the memory of the structure. + **/ +void +cdk_pkt_release (cdk_packet_t pkt) +{ + if (!pkt) + return; + cdk_pkt_free (pkt); + cdk_free (pkt); +} + + +/** + * cdk_pkt_alloc: + * @r_pkt: output is the new packet + * @pkttype: the requested packet type + * + * Allocate a new packet structure with the given packet type. + **/ +cdk_error_t +cdk_pkt_alloc (cdk_packet_t * r_pkt, int pkttype) +{ + cdk_packet_t pkt; + int rc; + + if (!r_pkt) + return CDK_Inv_Value; + + rc = cdk_pkt_new (&pkt); + if (rc) + return rc; + + switch (pkttype) + { + case CDK_PKT_USER_ID: + pkt->pkt.user_id = cdk_calloc (1, sizeof pkt->pkt.user_id); + if (!pkt->pkt.user_id) + return CDK_Out_Of_Core; + break; + + case CDK_PKT_PUBLIC_KEY: + case CDK_PKT_PUBLIC_SUBKEY: + pkt->pkt.public_key = cdk_calloc (1, sizeof *pkt->pkt.public_key); + if (!pkt->pkt.public_key) + return CDK_Out_Of_Core; + break; + + case CDK_PKT_SECRET_KEY: + case CDK_PKT_SECRET_SUBKEY: + pkt->pkt.secret_key = cdk_calloc (1, sizeof *pkt->pkt.secret_key); + pkt->pkt.secret_key->pk = + cdk_calloc (1, sizeof *pkt->pkt.secret_key->pk); + if (!pkt->pkt.secret_key || !pkt->pkt.secret_key->pk) + return CDK_Out_Of_Core; + break; + + case CDK_PKT_SIGNATURE: + pkt->pkt.signature = cdk_calloc (1, sizeof *pkt->pkt.signature); + if (!pkt->pkt.signature) + return CDK_Out_Of_Core; + break; + + case CDK_PKT_SYMKEY_ENC: + pkt->pkt.symkey_enc = cdk_calloc (1, sizeof *pkt->pkt.symkey_enc); + if (!pkt->pkt.symkey_enc) + return CDK_Out_Of_Core; + break; + + case CDK_PKT_PUBKEY_ENC: + pkt->pkt.pubkey_enc = cdk_calloc (1, sizeof *pkt->pkt.pubkey_enc); + if (!pkt->pkt.pubkey_enc) + return CDK_Out_Of_Core; + break; + + case CDK_PKT_MDC: + pkt->pkt.mdc = cdk_calloc (1, sizeof *pkt->pkt.mdc); + if (!pkt->pkt.mdc) + return CDK_Out_Of_Core; + break; + + case CDK_PKT_ENCRYPTED_MDC: + case CDK_PKT_ENCRYPTED: + pkt->pkt.symkey_enc = cdk_calloc (1, sizeof *pkt->pkt.symkey_enc); + if (!pkt->pkt.symkey_enc) + return CDK_Out_Of_Core; + break; + + case CDK_PKT_ONEPASS_SIG: + pkt->pkt.onepass_sig = cdk_calloc (1, sizeof *pkt->pkt.onepass_sig); + if (!pkt->pkt.onepass_sig) + return CDK_Out_Of_Core; + break; + + case CDK_PKT_LITERAL: + /* FIXME: We would need the size of the file name to allocate extra + bytes, otherwise the result would be useless. */ + pkt->pkt.literal = cdk_calloc (1, sizeof *pkt->pkt.literal); + if (!pkt->pkt.literal) + return CDK_Out_Of_Core; + break; + } + pkt->pkttype = pkttype; + *r_pkt = pkt; + return 0; +} + + +cdk_prefitem_t +_cdk_copy_prefs (const cdk_prefitem_t prefs) +{ + size_t n = 0; + struct cdk_prefitem_s *new_prefs; + + if (!prefs) + return NULL; + + for (n = 0; prefs[n].type; n++) + ; + new_prefs = cdk_calloc (1, sizeof *new_prefs * (n + 1)); + if (!new_prefs) + return NULL; + for (n = 0; prefs[n].type; n++) + { + new_prefs[n].type = prefs[n].type; + new_prefs[n].value = prefs[n].value; + } + new_prefs[n].type = CDK_PREFTYPE_NONE; + new_prefs[n].value = 0; + return new_prefs; +} + + +cdk_error_t +_cdk_copy_userid (cdk_pkt_userid_t * dst, cdk_pkt_userid_t src) +{ + cdk_pkt_userid_t u; + + if (!dst || !src) + return CDK_Inv_Value; + + *dst = NULL; + u = cdk_calloc (1, sizeof *u + strlen (src->name) + 1); + if (!u) + return CDK_Out_Of_Core; + memcpy (u, src, sizeof *u); + memcpy (u->name, src->name, strlen (src->name)); + u->prefs = _cdk_copy_prefs (src->prefs); + if (src->selfsig) + _cdk_copy_signature (&u->selfsig, src->selfsig); + *dst = u; + + return 0; +} + + +cdk_error_t +_cdk_copy_pubkey (cdk_pkt_pubkey_t * dst, cdk_pkt_pubkey_t src) +{ + cdk_pkt_pubkey_t k; + int i; + + if (!dst || !src) + return CDK_Inv_Value; + + *dst = NULL; + k = cdk_calloc (1, sizeof *k); + if (!k) + return CDK_Out_Of_Core; + memcpy (k, src, sizeof *k); + if (src->uid) + _cdk_copy_userid (&k->uid, src->uid); + if (src->prefs) + k->prefs = _cdk_copy_prefs (src->prefs); + for (i = 0; i < cdk_pk_get_npkey (src->pubkey_algo); i++) + k->mpi[i] = gcry_mpi_copy (src->mpi[i]); + *dst = k; + + return 0; +} + + +cdk_error_t +_cdk_copy_seckey (cdk_pkt_seckey_t * dst, cdk_pkt_seckey_t src) +{ + cdk_pkt_seckey_t k; + int i; + + if (!dst || !src) + return CDK_Inv_Value; + + *dst = NULL; + k = cdk_calloc (1, sizeof *k); + if (!k) + return CDK_Out_Of_Core; + memcpy (k, src, sizeof *k); + _cdk_copy_pubkey (&k->pk, src->pk); + + if (src->encdata) + { + k->encdata = cdk_calloc (1, src->enclen + 1); + if (!k->encdata) + return CDK_Out_Of_Core; + memcpy (k->encdata, src->encdata, src->enclen); + } + + _cdk_s2k_copy (&k->protect.s2k, src->protect.s2k); + + for (i = 0; i < cdk_pk_get_nskey (src->pubkey_algo); i++) + { + k->mpi[i] = gcry_mpi_copy (src->mpi[i]); + gcry_mpi_set_flag (k->mpi[i], GCRYMPI_FLAG_SECURE); + } + + *dst = k; + return 0; +} + + +cdk_error_t +_cdk_copy_pk_to_sk (cdk_pkt_pubkey_t pk, cdk_pkt_seckey_t sk) +{ + if (!pk || !sk) + return CDK_Inv_Value; + + sk->version = pk->version; + sk->expiredate = pk->expiredate; + sk->pubkey_algo = pk->pubkey_algo; + sk->has_expired = pk->has_expired; + sk->is_revoked = pk->is_revoked; + sk->main_keyid[0] = pk->main_keyid[0]; + sk->main_keyid[1] = pk->main_keyid[1]; + sk->keyid[0] = pk->keyid[0]; + sk->keyid[1] = pk->keyid[1]; + + return 0; +} + + +cdk_error_t +_cdk_copy_signature (cdk_pkt_signature_t * dst, cdk_pkt_signature_t src) +{ + cdk_pkt_signature_t s; + + if (!dst || !src) + return CDK_Inv_Value; + + *dst = NULL; + s = cdk_calloc (1, sizeof *s); + if (!s) + return CDK_Out_Of_Core; + memcpy (s, src, sizeof *src); + _cdk_subpkt_copy (&s->hashed, src->hashed); + _cdk_subpkt_copy (&s->unhashed, src->unhashed); + /* FIXME: Copy MPI parts */ + *dst = s; + + return 0; +} + + +cdk_error_t +_cdk_pubkey_compare (cdk_pkt_pubkey_t a, cdk_pkt_pubkey_t b) +{ + int na, nb, i; + + if (a->timestamp != b->timestamp || a->pubkey_algo != b->pubkey_algo) + return -1; + if (a->version < 4 && a->expiredate != b->expiredate) + return -1; + na = cdk_pk_get_npkey (a->pubkey_algo); + nb = cdk_pk_get_npkey (b->pubkey_algo); + if (na != nb) + return -1; + + for (i = 0; i < na; i++) + { + if (gcry_mpi_cmp (a->mpi[i], b->mpi[i])) + return -1; + } + + return 0; +} + + +/** + * cdk_subpkt_free: + * @ctx: the sub packet node to free + * + * Release the context. + **/ +void +cdk_subpkt_free (cdk_subpkt_t ctx) +{ + cdk_subpkt_t s; + + while (ctx) + { + s = ctx->next; + cdk_free (ctx); + ctx = s; + } +} + + +/** + * cdk_subpkt_find: + * @ctx: the sub packet node + * @type: the packet type to find + * + * Find the given packet type in the node. If no packet with this + * type was found, return null otherwise pointer to the node. + **/ +cdk_subpkt_t +cdk_subpkt_find (cdk_subpkt_t ctx, size_t type) +{ + return cdk_subpkt_find_nth (ctx, type, 0); +} + +/** + * cdk_subpkt_type_count: + * @ctx: The sub packet context + * @type: The sub packet type. + * + * Return the amount of sub packets with this type. + **/ +size_t +cdk_subpkt_type_count (cdk_subpkt_t ctx, size_t type) +{ + cdk_subpkt_t s; + size_t count; + + count = 0; + for (s = ctx; s; s = s->next) + { + if (s->type == type) + count++; + } + + return count; +} + + +/** + * cdk_subpkt_find_nth: + * @ctx: The sub packet context + * @type: The sub packet type + * @index: The nth packet to retrieve, 0 means the first + * + * Return the nth sub packet of the given type. + **/ +cdk_subpkt_t +cdk_subpkt_find_nth (cdk_subpkt_t ctx, size_t type, size_t idx) +{ + cdk_subpkt_t s; + size_t pos; + + pos = 0; + for (s = ctx; s; s = s->next) + { + if (s->type == type && pos++ == idx) + return s; + } + + return NULL; +} + + +/** + * cdk_subpkt_new: + * @size: the size of the new context + * + * Create a new sub packet node with the size of @size. + **/ +cdk_subpkt_t +cdk_subpkt_new (size_t size) +{ + cdk_subpkt_t s; + + if (!size) + return NULL; + s = cdk_calloc (1, sizeof *s + size + 1); + if (!s) + return NULL; + return s; +} + + +/** + * cdk_subpkt_get_data: + * @ctx: the sub packet node + * @r_type: pointer store the packet type + * @r_nbytes: pointer to store the packet size + * + * Extract the data from the given sub packet. The type is returned + * in @r_type and the size in @r_nbytes. + **/ +const byte * +cdk_subpkt_get_data (cdk_subpkt_t ctx, size_t * r_type, size_t * r_nbytes) +{ + if (!ctx || !r_nbytes) + return NULL; + if (r_type) + *r_type = ctx->type; + *r_nbytes = ctx->size; + return ctx->d; +} + + +/** + * cdk_subpkt_add: + * @root: the root node + * @node: the node to add + * + * Add the node in @node to the root node @root. + **/ +cdk_error_t +cdk_subpkt_add (cdk_subpkt_t root, cdk_subpkt_t node) +{ + cdk_subpkt_t n1; + + if (!root) + return CDK_Inv_Value; + for (n1 = root; n1->next; n1 = n1->next) + ; + n1->next = node; + return 0; +} + + +byte * +_cdk_subpkt_get_array (cdk_subpkt_t s, int count, size_t * r_nbytes) +{ + cdk_subpkt_t list; + byte *buf; + size_t n, nbytes; + + if (!s) + { + if (r_nbytes) + *r_nbytes = 0; + return NULL; + } + + for (n = 0, list = s; list; list = list->next) + { + n++; /* type */ + n += list->size; + if (list->size < 192) + n++; + else if (list->size < 8384) + n += 2; + else + n += 5; + } + buf = cdk_calloc (1, n + 1); + if (!buf) + return NULL; + + n = 0; + for (list = s; list; list = list->next) + { + nbytes = 1 + list->size; /* type */ + if (nbytes < 192) + buf[n++] = nbytes; + else if (nbytes < 8384) + { + buf[n++] = nbytes / 256 + 192; + buf[n++] = nbytes % 256; + } + else + { + buf[n++] = 0xFF; + buf[n++] = nbytes >> 24; + buf[n++] = nbytes >> 16; + buf[n++] = nbytes >> 8; + buf[n++] = nbytes; + } + buf[n++] = list->type; + memcpy (buf + n, list->d, list->size); + n += list->size; + } + + if (count) + { + cdk_free (buf); + buf = NULL; + } + if (r_nbytes) + *r_nbytes = n; + return buf; +} + + +cdk_error_t +_cdk_subpkt_copy (cdk_subpkt_t * r_dst, cdk_subpkt_t src) +{ + cdk_subpkt_t root, p, node; + + if (!src || !r_dst) + return CDK_Inv_Value; + + root = NULL; + for (p = src; p; p = p->next) + { + node = cdk_subpkt_new (p->size); + if (node) + { + memcpy (node->d, p->d, p->size); + node->type = p->type; + node->size = p->size; + } + if (!root) + root = node; + else + cdk_subpkt_add (root, node); + } + *r_dst = root; + return 0; +} + + +/** + * cdk_subpkt_init: + * @node: the sub packet node + * @type: type of the packet which data should be initialized + * @buf: the buffer with the actual data + * @buflen: the size of the data + * + * Set the packet data of the given root and set the type of it. + **/ +void +cdk_subpkt_init (cdk_subpkt_t node, size_t type, + const void *buf, size_t buflen) +{ + if (!node) + return; + node->type = type; + node->size = buflen; + memcpy (node->d, buf, buflen); +} + + +/* FIXME: We need to think of a public interface for it. */ +const byte * +cdk_key_desig_revoker_walk (cdk_desig_revoker_t root, + cdk_desig_revoker_t * ctx, + int *r_class, int *r_algid) +{ + cdk_desig_revoker_t n; + + if (!*ctx) + { + *ctx = root; + n = root; + } + else + { + n = (*ctx)->next; + *ctx = n; + } + + if (n && r_class && r_algid) + { + *r_class = n->r_class; + *r_algid = n->algid; + } + + return n ? n->fpr : NULL; +} + + +/** + * cdk_subpkt_find_next: + * @root: the base where to begin the iteration + * @type: the type to find or 0 for the next node. + * + * Try to find the next node after @root with type. + * If type is 0, the next node will be returned. + **/ +cdk_subpkt_t +cdk_subpkt_find_next (cdk_subpkt_t root, size_t type) +{ + cdk_subpkt_t node; + + for (node = root->next; node; node = node->next) + { + if (!type) + return node; + else if (node->type == type) + return node; + } + + return NULL; +} diff --git a/src/daemon/https/opencdk/opencdk.h b/src/daemon/https/opencdk/opencdk.h new file mode 100644 index 00000000..83ca487e --- /dev/null +++ b/src/daemon/https/opencdk/opencdk.h @@ -0,0 +1,1169 @@ +/* opencdk.h - Open Crypto Development Kit (OpenCDK) + * Copyright (C) 2001, 2002, 2003, 2007 Timo Schulz + * Copyright (C) 2006, 2007 Free Software Foundation, Inc. + * + * This file is part of OpenCDK. + * + * OpenCDK 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. + * + * OpenCDK 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. + */ + +#ifndef OPENCDK_H +#define OPENCDK_H + +#include /* for size_t */ +#include +#include + +/* The OpenCDK version as a string. */ +#define OPENCDK_VERSION "0.6.6" + +/* The OpenCDK version as integer components major.minor.path */ +#define OPENCDK_VERSION_MAJOR 0 +#define OPENCDK_VERSION_MINOR 6 +#define OPENCDK_VERSION_PATCH 6 + +#ifdef __cplusplus +extern "C" { +#if 0 +} +#endif +#endif + +/* General contexts */ + +/* 'Session' handle to support the various options and run-time + information. */ +struct cdk_ctx_s; +typedef struct cdk_ctx_s *cdk_ctx_t; + +/* A generic context to store list of strings. */ +struct cdk_strlist_s; +typedef struct cdk_strlist_s *cdk_strlist_t; + +/* Context used to list keys of a keyring. */ +struct cdk_listkey_s; +typedef struct cdk_listkey_s *cdk_listkey_t; + +/* Opaque Data Encryption Key (DEK) context. */ +struct cdk_dek_s; +typedef struct cdk_dek_s *cdk_dek_t; + +/* Opaque String to Key (S2K) handle. */ +struct cdk_s2k_s; +typedef struct cdk_s2k_s *cdk_s2k_t; + +/* Abstract I/O object, a stream, which is used for most operations. */ +struct cdk_stream_s; +typedef struct cdk_stream_s *cdk_stream_t; + +/* Opaque handle for the user ID preferences. */ +struct cdk_prefitem_s; +typedef struct cdk_prefitem_s *cdk_prefitem_t; + +/* Node to store a single key node packet. */ +struct cdk_kbnode_s; +typedef struct cdk_kbnode_s *cdk_kbnode_t; + +/* Key database handle. */ +struct cdk_keydb_hd_s; +typedef struct cdk_keydb_hd_s *cdk_keydb_hd_t; + +/* Context to store a list of recipient keys. */ +struct cdk_keylist_s; +typedef struct cdk_keylist_s *cdk_keylist_t; + +/* Context to encapsulate a single sub packet of a signature. */ +struct cdk_subpkt_s; +typedef struct cdk_subpkt_s *cdk_subpkt_t; + +/* Context used to generate key pairs. */ +struct cdk_keygen_ctx_s; +typedef struct cdk_keygen_ctx_s *cdk_keygen_ctx_t; + +/* Handle for a single designated revoker. */ +struct cdk_desig_revoker_s; +typedef struct cdk_desig_revoker_s *cdk_desig_revoker_t; + +/* Alias for backward compatibility. */ +typedef gcry_mpi_t cdk_mpi_t; + + +/* All valid error constants. */ +typedef enum { + CDK_EOF = -1, + CDK_Success = 0, + CDK_General_Error = 1, + CDK_File_Error = 2, + CDK_Bad_Sig = 3, + CDK_Inv_Packet = 4, + CDK_Inv_Algo = 5, + CDK_Not_Implemented = 6, + CDK_Armor_Error = 8, + CDK_Armor_CRC_Error = 9, + CDK_MPI_Error = 10, + CDK_Inv_Value = 11, + CDK_Error_No_Key = 12, + CDK_Chksum_Error = 13, + CDK_Time_Conflict = 14, + CDK_Zlib_Error = 15, + CDK_Weak_Key = 16, + CDK_Out_Of_Core = 17, + CDK_Wrong_Seckey = 18, + CDK_Bad_MDC = 19, + CDK_Inv_Mode = 20, + CDK_Error_No_Keyring = 21, + CDK_Wrong_Format = 22, + CDK_Inv_Packet_Ver = 23, + CDK_Too_Short = 24, + CDK_Unusable_Key = 25, + CDK_No_Data = 26, + CDK_No_Passphrase = 27, + CDK_Network_Error = 28 +} cdk_error_t; + + +enum cdk_control_flags { + CDK_CTLF_SET = 0, /* Value to set an option */ + CDK_CTLF_GET = 1, /* Value to get an option */ + CDK_CTL_DIGEST = 10, /* Option to set the digest algorithm. */ + CDK_CTL_CIPHER = 11, /* Option to set the cipher algorithm. */ + CDK_CTL_ARMOR = 12, /* Option to enable armor output. */ + CDK_CTL_COMPRESS = 13, /* Option to enable compression. */ + CDK_CTL_COMPAT = 14, /* Option to switch in compat mode. */ + CDK_CTL_OVERWRITE = 15, /* Option to enable file overwritting. */ + CDK_CTL_S2K = 16, /* Option to set S2K values. */ + CDK_CTL_FORCE_DIGEST = 19, /* Force the use of a digest algorithm. */ + CDK_CTL_BLOCKMODE_ON = 20 /* Enable partial body lengths */ +}; + + +/* Specifies all valid log levels. */ +enum cdk_log_level_t { + CDK_LOG_NONE = 0, /* No log message will be shown. */ + CDK_LOG_INFO = 1, + CDK_LOG_DEBUG = 2, + CDK_LOG_DEBUG_PKT = 3 +}; + + +/* All valid compression algorithms in OpenPGP */ +enum cdk_compress_algo_t { + CDK_COMPRESS_NONE = 0, + CDK_COMPRESS_ZIP = 1, + CDK_COMPRESS_ZLIB = 2, + CDK_COMPRESS_BZIP2 = 3 /* Not supported in this version */ +}; + + +/* All valid public key algorithms valid in OpenPGP */ +enum cdk_pubkey_algo_t { + CDK_PK_RSA = 1, + CDK_PK_RSA_E = 2, /* RSA-E and RSA-S are deprecated use RSA instead */ + CDK_PK_RSA_S = 3, /* and use the key flags in the self signatures. */ + CDK_PK_ELG_E = 16, + CDK_PK_DSA = 17 +}; + + +/* All valid message digest algorithms in OpenPGP. */ +enum cdk_digest_algo_t { + CDK_MD_NONE = 0, + CDK_MD_MD5 = 1, + CDK_MD_SHA1 = 2, + CDK_MD_RMD160 = 3, + CDK_MD_SHA256 = 8, + CDK_MD_SHA384 = 9, + CDK_MD_SHA512 = 10, + CDK_MD_SHA224 = 11 /* This algorithm is NOT available. */ +}; + + +/* All valid symmetric cipher algorithms in OpenPGP */ +enum cdk_cipher_algo_t { + CDK_CIPHER_NONE = 0, + CDK_CIPHER_IDEA = 1, /* This algorithm is NOT available */ + CDK_CIPHER_3DES = 2, + CDK_CIPHER_CAST5 = 3, + CDK_CIPHER_BLOWFISH = 4, + CDK_CIPHER_AES = 7, + CDK_CIPHER_AES192 = 8, + CDK_CIPHER_AES256 = 9, + CDK_CIPHER_TWOFISH = 10 +}; + + +/* The valid 'String-To-Key' modes */ +enum cdk_s2k_type_t { + CDK_S2K_SIMPLE = 0, + CDK_S2K_SALTED = 1, + CDK_S2K_ITERSALTED = 3 +}; + + +/* The different kind of user ID preferences. */ +enum cdk_pref_type_t { + CDK_PREFTYPE_NONE = 0, + CDK_PREFTYPE_SYM = 1, /* Symmetric ciphers */ + CDK_PREFTYPE_HASH = 2, /* Message digests */ + CDK_PREFTYPE_ZIP = 3 /* Compression algorithms */ +}; + + +/* All valid sub packet types. */ +enum cdk_sig_subpacket_t { + CDK_SIGSUBPKT_NONE = 0, + CDK_SIGSUBPKT_SIG_CREATED = 2, + CDK_SIGSUBPKT_SIG_EXPIRE = 3, + CDK_SIGSUBPKT_EXPORTABLE = 4, + CDK_SIGSUBPKT_TRUST = 5, + CDK_SIGSUBPKT_REGEXP = 6, + CDK_SIGSUBPKT_REVOCABLE = 7, + CDK_SIGSUBPKT_KEY_EXPIRE = 9, + CDK_SIGSUBPKT_PREFS_SYM = 11, + CDK_SIGSUBPKT_REV_KEY = 12, + CDK_SIGSUBPKT_ISSUER = 16, + CDK_SIGSUBPKT_NOTATION = 20, + CDK_SIGSUBPKT_PREFS_HASH = 21, + CDK_SIGSUBPKT_PREFS_ZIP = 22, + CDK_SIGSUBPKT_KS_FLAGS = 23, + CDK_SIGSUBPKT_PREF_KS = 24, + CDK_SIGSUBPKT_PRIMARY_UID = 25, + CDK_SIGSUBPKT_POLICY = 26, + CDK_SIGSUBPKT_KEY_FLAGS = 27, + CDK_SIGSUBPKT_SIGNERS_UID = 28, + CDK_SIGSUBPKT_REVOC_REASON = 29, + CDK_SIGSUBPKT_FEATURES = 30 +}; + + +/* All valid armor types. */ +enum cdk_armor_type_t { + CDK_ARMOR_MESSAGE = 0, + CDK_ARMOR_PUBKEY = 1, + CDK_ARMOR_SECKEY = 2, + CDK_ARMOR_SIGNATURE = 3, + CDK_ARMOR_CLEARSIG = 4 +}; + +enum cdk_keydb_flag_t { + /* Valid database search modes */ + CDK_DBSEARCH_EXACT = 1, /* Exact string search */ + CDK_DBSEARCH_SUBSTR = 2, /* Sub string search */ + CDK_DBSEARCH_SHORT_KEYID = 3, /* 32-bit keyid search */ + CDK_DBSEARCH_KEYID = 4, /* 64-bit keyid search */ + CDK_DBSEARCH_FPR = 5, /* 160-bit fingerprint search */ + CDK_DBSEARCH_NEXT = 6, /* Enumerate all keys */ + CDK_DBSEARCH_AUTO = 7, /* Try to classify the string */ + /* Valid database types */ + CDK_DBTYPE_PK_KEYRING = 100, /* A file with one or more public keys */ + CDK_DBTYPE_SK_KEYRING = 101, /* A file with one or more secret keys */ + CDK_DBTYPE_DATA = 102, /* A buffer with at least one public key */ + CDK_DBTYPE_STREAM = 103 /* A stream is used to read keys from */ +}; + + +/* All valid modes for cdk_data_transform() */ +enum cdk_crypto_mode_t { + CDK_CRYPTYPE_NONE = 0, + CDK_CRYPTYPE_ENCRYPT = 1, + CDK_CRYPTYPE_DECRYPT = 2, + CDK_CRYPTYPE_SIGN = 3, + CDK_CRYPTYPE_VERIFY = 4, + CDK_CRYPTYPE_EXPORT = 5, + CDK_CRYPTYPE_IMPORT = 6 +}; + + +/* A list of valid public key usages. */ +enum cdk_key_usage_t { + CDK_KEY_USG_ENCR = 1, /* Key can be used for encryption. */ + CDK_KEY_USG_SIGN = 2, /* Key can be used for signing and certifying. */ + CDK_KEY_USG_AUTH = 4 /* Key can be used for authentication. */ +}; + + +/* Valid flags for keys. */ +enum cdk_key_flag_t { + CDK_KEY_VALID = 0, + CDK_KEY_INVALID = 1, /* Missing or wrong self signature */ + CDK_KEY_EXPIRED = 2, /* Key is expired. */ + CDK_KEY_REVOKED = 4, /* Key has been revoked. */ + CDK_KEY_NOSIGNER = 8 +}; + + +/* Trust values and flags for keys and user IDs */ +enum cdk_trust_flag_t { + CDK_TRUST_UNKNOWN = 0, + CDK_TRUST_EXPIRED = 1, + CDK_TRUST_UNDEFINED = 2, + CDK_TRUST_NEVER = 3, + CDK_TRUST_MARGINAL = 4, + CDK_TRUST_FULLY = 5, + CDK_TRUST_ULTIMATE = 6, + /* trust flags */ + CDK_TFLAG_REVOKED = 32, + CDK_TFLAG_SUB_REVOKED = 64, + CDK_TFLAG_DISABLED = 128 +}; + + +/* Signature states and the signature modes. */ +enum cdk_signature_stat_t { + /* Signature status */ + CDK_SIGSTAT_NONE = 0, + CDK_SIGSTAT_GOOD = 1, + CDK_SIGSTAT_BAD = 2, + CDK_SIGSTAT_NOKEY = 3, + CDK_SIGSTAT_VALID = 4, /* True if made with a valid key. */ + /* FIXME: We need indicators for revoked/expires signatures. */ + + /* Signature modes */ + CDK_SIGMODE_NORMAL = 100, + CDK_SIGMODE_DETACHED = 101, + CDK_SIGMODE_CLEAR = 102 +}; + + +/* Key flags. */ +typedef enum { + CDK_FLAG_KEY_REVOKED = 256, + CDK_FLAG_KEY_EXPIRED = 512, + CDK_FLAG_SIG_EXPIRED = 1024 +} cdk_key_flags_t; + + +/* Possible format for the literal data. */ +typedef enum { + CDK_LITFMT_BINARY = 0, + CDK_LITFMT_TEXT = 1, + CDK_LITFMT_UNICODE= 2 +} cdk_lit_format_t; + +/* Valid OpenPGP packet types and their IDs */ +typedef enum { + CDK_PKT_RESERVED = 0, + CDK_PKT_PUBKEY_ENC = 1, + CDK_PKT_SIGNATURE = 2, + CDK_PKT_SYMKEY_ENC = 3, + CDK_PKT_ONEPASS_SIG = 4, + CDK_PKT_SECRET_KEY = 5, + CDK_PKT_PUBLIC_KEY = 6, + CDK_PKT_SECRET_SUBKEY = 7, + CDK_PKT_COMPRESSED = 8, + CDK_PKT_ENCRYPTED = 9, + CDK_PKT_MARKER = 10, + CDK_PKT_LITERAL = 11, + CDK_PKT_RING_TRUST = 12, + CDK_PKT_USER_ID = 13, + CDK_PKT_PUBLIC_SUBKEY = 14, + CDK_PKT_OLD_COMMENT = 16, + CDK_PKT_ATTRIBUTE = 17, + CDK_PKT_ENCRYPTED_MDC = 18, + CDK_PKT_MDC = 19 +} cdk_packet_type_t; + +/* Define the maximal number of multiprecion integers for + a public key. */ +#define MAX_CDK_PK_PARTS 4 + +/* Define the maximal number of multiprecision integers for + a signature/encrypted blob issued by a secret key. */ +#define MAX_CDK_DATA_PARTS 2 + + +/* Helper macro to figure out if the packet is encrypted */ +#define CDK_PKT_IS_ENCRYPTED(pkttype) (\ + ((pkttype)==CDK_PKT_ENCRYPTED_MDC) \ + || ((pkttype)==CDK_PKT_ENCRYPTED)) + + +struct cdk_pkt_signature_s { + unsigned char version; + unsigned char sig_class; + unsigned int timestamp; + unsigned int expiredate; + unsigned int keyid[2]; + unsigned char pubkey_algo; + unsigned char digest_algo; + unsigned char digest_start[2]; + unsigned short hashed_size; + cdk_subpkt_t hashed; + unsigned short unhashed_size; + cdk_subpkt_t unhashed; + gcry_mpi_t mpi[MAX_CDK_DATA_PARTS]; + cdk_desig_revoker_t revkeys; + struct { + unsigned exportable:1; + unsigned revocable:1; + unsigned policy_url:1; + unsigned notation:1; + unsigned expired:1; + unsigned checked:1; + unsigned valid:1; + unsigned missing_key:1; + } flags; + unsigned int key[2]; /* only valid for key signatures */ +}; +typedef struct cdk_pkt_signature_s *cdk_pkt_signature_t; + + +struct cdk_pkt_userid_s { + unsigned int len; + unsigned is_primary:1; + unsigned is_revoked:1; + unsigned mdc_feature:1; + cdk_prefitem_t prefs; + size_t prefs_size; + unsigned char * attrib_img; /* Tag 17 if not null */ + size_t attrib_len; + cdk_pkt_signature_t selfsig; + char name[1]; +}; +typedef struct cdk_pkt_userid_s *cdk_pkt_userid_t; + + +struct cdk_pkt_pubkey_s { + unsigned char version; + unsigned char pubkey_algo; + unsigned char fpr[20]; + unsigned int keyid[2]; + unsigned int main_keyid[2]; + unsigned int timestamp; + unsigned int expiredate; + gcry_mpi_t mpi[MAX_CDK_PK_PARTS]; + unsigned is_revoked:1; + unsigned is_invalid:1; + unsigned has_expired:1; + int pubkey_usage; + cdk_pkt_userid_t uid; + cdk_prefitem_t prefs; + size_t prefs_size; + cdk_desig_revoker_t revkeys; +}; +typedef struct cdk_pkt_pubkey_s *cdk_pkt_pubkey_t; + +/* Alias to define a generic public key context. */ +typedef cdk_pkt_pubkey_t cdk_pubkey_t; + + +struct cdk_pkt_seckey_s { + cdk_pkt_pubkey_t pk; + unsigned int expiredate; + int version; + int pubkey_algo; + unsigned int keyid[2]; + unsigned int main_keyid[2]; + unsigned char s2k_usage; + struct { + unsigned char algo; + unsigned char sha1chk; /* SHA1 is used instead of a 16 bit checksum */ + cdk_s2k_t s2k; + unsigned char iv[16]; + unsigned char ivlen; + } protect; + unsigned short csum; + gcry_mpi_t mpi[MAX_CDK_PK_PARTS]; + unsigned char * encdata; + size_t enclen; + unsigned char is_protected; + unsigned is_primary:1; + unsigned has_expired:1; + unsigned is_revoked:1; +}; +typedef struct cdk_pkt_seckey_s *cdk_pkt_seckey_t; + +/* Alias to define a generic secret key context. */ +typedef cdk_pkt_seckey_t cdk_seckey_t; + + +struct cdk_pkt_onepass_sig_s { + unsigned char version; + unsigned int keyid[2]; + unsigned char sig_class; + unsigned char digest_algo; + unsigned char pubkey_algo; + unsigned char last; +}; +typedef struct cdk_pkt_onepass_sig_s * cdk_pkt_onepass_sig_t; + + +struct cdk_pkt_pubkey_enc_s { + unsigned char version; + unsigned int keyid[2]; + int throw_keyid; + unsigned char pubkey_algo; + gcry_mpi_t mpi[MAX_CDK_DATA_PARTS]; +}; +typedef struct cdk_pkt_pubkey_enc_s * cdk_pkt_pubkey_enc_t; + + +struct cdk_pkt_symkey_enc_s { + unsigned char version; + unsigned char cipher_algo; + cdk_s2k_t s2k; + unsigned char seskeylen; + unsigned char seskey[32]; +}; +typedef struct cdk_pkt_symkey_enc_s *cdk_pkt_symkey_enc_t; + + +struct cdk_pkt_encrypted_s { + unsigned int len; + int extralen; + unsigned char mdc_method; + cdk_stream_t buf; +}; +typedef struct cdk_pkt_encrypted_s *cdk_pkt_encrypted_t; + + +struct cdk_pkt_mdc_s { + unsigned char hash[20]; +}; +typedef struct cdk_pkt_mdc_s *cdk_pkt_mdc_t; + + +struct cdk_pkt_literal_s { + unsigned int len; + cdk_stream_t buf; + int mode; + unsigned int timestamp; + int namelen; + char name[1]; +}; +typedef struct cdk_pkt_literal_s *cdk_pkt_literal_t; + + +struct cdk_pkt_compressed_s { + unsigned int len; + int algorithm; + cdk_stream_t buf; +}; +typedef struct cdk_pkt_compressed_s *cdk_pkt_compressed_t; + + +/* Structure which represents a single OpenPGP packet. */ +struct cdk_packet_s { + size_t pktlen; /* real packet length */ + size_t pktsize; /* length with all headers */ + int old_ctb; /* 1 if RFC1991 mode is used */ + cdk_packet_type_t pkttype; + union { + cdk_pkt_mdc_t mdc; + cdk_pkt_userid_t user_id; + cdk_pkt_pubkey_t public_key; + cdk_pkt_seckey_t secret_key; + cdk_pkt_signature_t signature; + cdk_pkt_pubkey_enc_t pubkey_enc; + cdk_pkt_symkey_enc_t symkey_enc; + cdk_pkt_compressed_t compressed; + cdk_pkt_encrypted_t encrypted; + cdk_pkt_literal_t literal; + cdk_pkt_onepass_sig_t onepass_sig; + } pkt; +}; +typedef struct cdk_packet_s *cdk_packet_t; + +/* memory routines */ +typedef void (*cdk_log_fnc_t) (void *, int, const char *, va_list); + +/* Set the log level. */ +void cdk_set_log_level (int lvl); + +/* Set a custom log handler which is used for logging. */ +void cdk_set_log_handler (cdk_log_fnc_t logfnc, void *opaque); + +/* Return a human readable error description of the given error coe. */ +const char* cdk_strerror (int ec); + +/* Allow the user to set custom hooks for memory allocation. + If this function is not used, the standard allocation functions + will be used. Extra care must be taken for the 'secure' alloc + function. */ +void cdk_set_malloc_hooks (void *(*new_alloc_func) (size_t n), + void *(*new_alloc_secure_func) (size_t n), + void *(*new_realloc_func) (void *p, size_t n), + void *(*new_calloc_func) (size_t m, size_t n), + void (*new_free_func) (void *)); + +/* Return 1 if the malloc hooks were already set via the function above. */ +int cdk_malloc_hook_initialized (void); + +/* Standard memory wrapper. */ +void *cdk_malloc (size_t size); +void *cdk_calloc (size_t n, size_t m); +void *cdk_realloc (void * ptr, size_t size); +void *cdk_salloc (size_t size, int clear); +char *cdk_strdup (const char * ptr); +void cdk_free (void * ptr); + +/* Startup routines. */ + +/* This function has to be called before any other + CDK function is executed. */ +void cdk_lib_startup (void); + +/* This function should be called before the application exists + to allow the lib to cleanup its internal structures. */ +void cdk_lib_shutdown (void); + +/* Session handle routines */ +cdk_error_t cdk_handle_new (cdk_ctx_t *r_ctx); +void cdk_handle_free (cdk_ctx_t c); + +/* Set the key database handle for the given session handle. + The type of the key db handle (public or secret) decides + which session key db handle to use. */ +void cdk_handle_set_keydb (cdk_ctx_t hd, cdk_keydb_hd_t db); + +/* Convenient function to avoid to open a key db first. + The user can directly use the file name, the rest is + done internally. */ +cdk_error_t cdk_handle_set_keyring (cdk_ctx_t hd, int type, + const char *kringname); + +/* Return keydb handle stored in the session handle. */ +cdk_keydb_hd_t cdk_handle_get_keydb (cdk_ctx_t hd, int type); +int cdk_handle_control (cdk_ctx_t hd, int action, int cmd, ...); + +/* Set a passphrase callback for the given session handle. */ +void cdk_handle_set_passphrase_cb (cdk_ctx_t hd, + char *(*cb) (void *opa, const char *prompt), + void *cb_value); + +/* shortcuts for some controls */ + +/* Enable or disable armor output. */ +#define cdk_handle_set_armor(a, val) \ + cdk_handle_control ((a), CDK_CTLF_SET, CDK_CTL_ARMOR, (val)) + +/* Set the compression algorithm and level. 0 means disable compression. */ +#define cdk_handle_set_compress(a, algo, level) \ + cdk_handle_control ((a), CDK_CTLF_SET, CDK_CTL_COMPRESS, (algo), (level)) + +/* Activate partial bodies for the output. This is needed if the length + of the data is not known in advance or for the use with sockets + or pipes. */ +#define cdk_handle_set_blockmode(a, val) \ + cdk_handle_control ((a), CDK_CTLF_SET, CDK_CTL_BLOCKMODE_ON, (val)) + +/* Set the symmetric cipher for encryption. */ +#define cdk_handle_set_cipher(a, val) \ + cdk_handle_control ((a), CDK_CTLF_SET, CDK_CTL_CIPHER, (val)) + +/* Set the digest for the PK signing operation. */ +#define cdk_handle_set_digest(a, val) \ + cdk_handle_control ((a), CDK_CTLF_SET, CDK_CTL_DIGEST, (val)) + +/* Set the mode and the digest for the S2K operation. */ +#define cdk_handle_set_s2k(a, val1, val2) \ + cdk_handle_control ((a), CDK_CTLF_SET, CDK_CTL_S2K, (val1), (val2)) + + +/* This context holds all information of the verification process. */ +struct cdk_verify_result_s { + int sig_ver; /* Version of the signature. */ + int sig_status; /* The status (GOOD, BAD) of the signature */ + int sig_flags; /* May contain expired or revoked flags */ + unsigned int keyid[2]; /* The issuer key ID */ + unsigned int created; /* Timestamp when the sig was created. */ + unsigned int expires; + int pubkey_algo; + int digest_algo; + char *user_id; /* NULL or user ID which issued the signature. */ + char *policy_url; /* If set, the policy the sig was created under. */ + size_t sig_len; /* Size of the signature data inbits. */ + unsigned char *sig_data; /* Raw signature data. */ +}; +typedef struct cdk_verify_result_s *cdk_verify_result_t; + +/* Return the verify result. Do not free the data. */ +cdk_verify_result_t cdk_handle_verify_get_result (cdk_ctx_t hd); + +/* Raw packet routines. */ + +/* Allocate a new packet or a new packet with the given packet type. */ +cdk_error_t cdk_pkt_new (cdk_packet_t *r_pkt); +cdk_error_t cdk_pkt_alloc (cdk_packet_t *r_pkt, int pkttype); + +/* Only release the contents of the packet but not @PKT itself. */ +void cdk_pkt_free (cdk_packet_t pkt); + +/* Release the packet contents and the packet structure @PKT itself. */ +void cdk_pkt_release (cdk_packet_t pkt); + +/* Read or write the given output from or to the stream. */ +cdk_error_t cdk_pkt_read (cdk_stream_t inp, cdk_packet_t pkt); +cdk_error_t cdk_pkt_write (cdk_stream_t out, cdk_packet_t pkt); + +/* Sub packet routines */ +cdk_subpkt_t cdk_subpkt_new (size_t size); +void cdk_subpkt_free (cdk_subpkt_t ctx); +cdk_subpkt_t cdk_subpkt_find (cdk_subpkt_t ctx, size_t type); +cdk_subpkt_t cdk_subpkt_find_next (cdk_subpkt_t root, size_t type); +size_t cdk_subpkt_type_count (cdk_subpkt_t ctx, size_t type); +cdk_subpkt_t cdk_subpkt_find_nth (cdk_subpkt_t ctx, size_t type, size_t index); +cdk_error_t cdk_subpkt_add (cdk_subpkt_t root, cdk_subpkt_t node); +const unsigned char * cdk_subpkt_get_data (cdk_subpkt_t ctx, + size_t * r_type, size_t *r_nbytes); +void cdk_subpkt_init (cdk_subpkt_t node, size_t type, + const void *buf, size_t buflen); + +/* Designated Revoker routines */ +const unsigned char* cdk_key_desig_revoker_walk (cdk_desig_revoker_t root, + cdk_desig_revoker_t * ctx, + int *r_class, int *r_algid); + +#define is_RSA(a) ((a) == CDK_PK_RSA \ + || (a) == CDK_PK_RSA_E \ + || (a) == CDK_PK_RSA_S) +#define is_ELG(a) ((a) == CDK_PK_ELG_E) +#define is_DSA(a) ((a) == CDK_PK_DSA) + +/* Encrypt the given session key @SK with the public key @PK + and write the contents into the packet @PKE. */ +cdk_error_t cdk_pk_encrypt (cdk_pubkey_t pk, cdk_pkt_pubkey_enc_t pke, + gcry_mpi_t sk); + +/* Decrypt the given encrypted session key in @PKE with the secret key + @SK and store it in @R_SK. */ +cdk_error_t cdk_pk_decrypt (cdk_seckey_t sk, cdk_pkt_pubkey_enc_t pke, + gcry_mpi_t *r_sk); + +/* Sign the given message digest @MD with the secret key @SK and + store the signature in the packet @SIG. */ +cdk_error_t cdk_pk_sign (cdk_seckey_t sk, cdk_pkt_signature_t sig, + const unsigned char *md); + +/* Verify the given signature in @SIG with the public key @PK + and compare it against the message digest @MD. */ +cdk_error_t cdk_pk_verify (cdk_pubkey_t pk, cdk_pkt_signature_t sig, + const unsigned char *md); + +/* Use cdk_pk_get_npkey() and cdk_pk_get_nskey to find out how much + multiprecision integers a key consists of. */ + +/* Return a multi precision integer of the public key with the index @IDX + in the buffer @BUF. @R_NWRITTEN will contain the length in octets. + Optional @R_NBITS may contain the size in bits. */ +cdk_error_t cdk_pk_get_mpi (cdk_pubkey_t pk, size_t idx, + unsigned char *buf, size_t buflen, + size_t *r_nwritten, size_t *r_nbits); + +/* Same as the function above but of the secret key. */ +cdk_error_t cdk_sk_get_mpi (cdk_seckey_t sk, size_t idx, + unsigned char * buf, size_t buflen, + size_t *r_nwritten, size_t *r_nbits); + +/* Helper to get the exact number of multi precision integers + for the given object. */ +int cdk_pk_get_nbits (cdk_pubkey_t pk); +int cdk_pk_get_npkey (int algo); +int cdk_pk_get_nskey (int algo); +int cdk_pk_get_nsig (int algo); +int cdk_pk_get_nenc (int algo); + +/* Fingerprint and key ID routines. */ + +/* Calculate the fingerprint of the given public key. + the FPR parameter must be at least 20 octets to hold the SHA1 hash. */ +cdk_error_t cdk_pk_get_fingerprint (cdk_pubkey_t pk, unsigned char *fpr); + +/* Same as above, but with additional sanity checks of the buffer size. */ +cdk_error_t cdk_pk_to_fingerprint (cdk_pubkey_t pk, + unsigned char *fpr, size_t fprlen, + size_t *r_nout); + +/* Derive the keyid from the fingerprint. This is only possible for + modern, version 4 keys. */ +unsigned int cdk_pk_fingerprint_get_keyid (const unsigned char *fpr, + size_t fprlen, + unsigned int *keyid); + +/* Various functions to get the keyid from the specific packet type. */ +unsigned int cdk_pk_get_keyid (cdk_pubkey_t pk, unsigned int *keyid); +unsigned int cdk_sk_get_keyid (cdk_seckey_t sk, unsigned int *keyid); +unsigned int cdk_sig_get_keyid (cdk_pkt_signature_t sig, + unsigned int *keyid); + +/* Key release functions. */ +void cdk_pk_release (cdk_pubkey_t pk); +void cdk_sk_release (cdk_seckey_t sk); + +/* Secret key related functions. */ +cdk_error_t cdk_sk_unprotect (cdk_seckey_t sk, const char *pw); +cdk_error_t cdk_sk_protect (cdk_seckey_t sk, const char *pw); + +/* Create a public key with the data from the secret key @SK. */ +cdk_error_t cdk_pk_from_secret_key (cdk_seckey_t sk, + cdk_pubkey_t *ret_pk); + +/* Sexp conversion of keys. */ +cdk_error_t cdk_pubkey_to_sexp (cdk_pubkey_t pk, + char **sexp, size_t *len); +cdk_error_t cdk_seckey_to_sexp (cdk_seckey_t sk, + char **sexp, size_t *len); + + +/* Data Encryption Key (DEK) routines */ +cdk_error_t cdk_dek_new (cdk_dek_t *r_dek); +void cdk_dek_free (cdk_dek_t dek); + +/* Set the symmetric cipher algorithm which shall be used for this + DEK object. */ +cdk_error_t cdk_dek_set_cipher (cdk_dek_t dek, int cipher_algo); + +/* Return the symmetric cipher which is used for this DEK object. */ +cdk_error_t cdk_dek_get_cipher (cdk_dek_t dek, int *r_cipher_algo); + + +/* Set the session key for the given DEK object. + If @KEY and @KEYLEN are both NULL/0, a random key will be generated + and stored in the DEK object. */ +cdk_error_t cdk_dek_set_key (cdk_dek_t dek, const unsigned char *key, + size_t keylen); + +/* Enable the MDC feature for the current DEK object. */ +void cdk_dek_set_mdc_flag (cdk_dek_t dek, int val); + +/* Return if the MDC feature is enabled for the current DEK object.*/ +int cdk_dek_get_mdc_flag (cdk_dek_t dek); + +/* Transform the given passphrase into a DEK. + If @rndsalt is set, a random salt will be generated. */ +cdk_error_t cdk_dek_from_passphrase (cdk_dek_t *ret_dek, int cipher_algo, + cdk_s2k_t s2k, int rndsalt, + const char *passphrase); + +/* String to Key routines. */ +cdk_error_t cdk_s2k_new (cdk_s2k_t *ret_s2k, int mode, int digest_algo, + const unsigned char *salt); +void cdk_s2k_free (cdk_s2k_t s2k); + +cdk_error_t cdk_file_armor (cdk_ctx_t hd, const char *file, + const char *output); +cdk_error_t cdk_file_dearmor (const char * file, const char *output); +int cdk_armor_filter_use (cdk_stream_t inp); + +/* Protect the inbuf with ASCII armor of the specified type. + If @outbuf and @outlen are NULL, the function returns the calculated + size of the base64 encoded data in @nwritten. */ +cdk_error_t cdk_armor_encode_buffer (const unsigned char *inbuf, size_t inlen, + char *outbuf, size_t outlen, + size_t *nwritten, int type); + + +/* This context contain user callbacks for different stream operations. + Some of these callbacks might be NULL to indicate that the callback + is not used. */ +struct cdk_stream_cbs_s +{ + cdk_error_t (*open)(void *); + cdk_error_t (*release)(void *); + int (*read)(void *, void *buf, size_t); + int (*write)(void *, const void *buf, size_t); + int (*seek)(void *, off_t); +}; +typedef struct cdk_stream_cbs_s *cdk_stream_cbs_t; + + +int cdk_stream_is_compressed (cdk_stream_t s); + +/* Return a stream object which is associated to a socket. */ +cdk_error_t cdk_stream_sockopen (const char *host, unsigned short port, + cdk_stream_t *ret_out); + +/* Return a stream object which is associated to an existing file. */ +cdk_error_t cdk_stream_open (const char * file, cdk_stream_t * ret_s); + +/* Return a stream object which is associated to a file which will + be created when the stream is closed. */ +cdk_error_t cdk_stream_new (const char * file, cdk_stream_t * ret_s); + +/* Return a stream object with custom callback functions for the + various core operations. */ +cdk_error_t cdk_stream_new_from_cbs (cdk_stream_cbs_t cbs, void *opa, + cdk_stream_t *ret_s); +cdk_error_t cdk_stream_create (const char * file, cdk_stream_t * ret_s); +cdk_error_t cdk_stream_tmp_new (cdk_stream_t *r_out); +cdk_error_t cdk_stream_tmp_from_mem (const void * buf, size_t buflen, + cdk_stream_t *r_out); +void cdk_stream_tmp_set_mode (cdk_stream_t s, int val); +cdk_error_t cdk_stream_flush (cdk_stream_t s); +cdk_error_t cdk_stream_enable_cache (cdk_stream_t s, int val); +cdk_error_t cdk_stream_filter_disable (cdk_stream_t s, int type); +cdk_error_t cdk_stream_close (cdk_stream_t s); +off_t cdk_stream_get_length (cdk_stream_t s); +int cdk_stream_read (cdk_stream_t s, void * buf, size_t count); +int cdk_stream_write (cdk_stream_t s, const void * buf, size_t count); +int cdk_stream_putc (cdk_stream_t s, int c); +int cdk_stream_getc (cdk_stream_t s); +int cdk_stream_eof (cdk_stream_t s); +off_t cdk_stream_tell (cdk_stream_t s); +cdk_error_t cdk_stream_seek (cdk_stream_t s, off_t offset); +cdk_error_t cdk_stream_set_armor_flag (cdk_stream_t s, int type); + +/* Push the literal filter for the given stream. */ +cdk_error_t cdk_stream_set_literal_flag (cdk_stream_t s, + cdk_lit_format_t mode, + const char * fname); + +cdk_error_t cdk_stream_set_cipher_flag (cdk_stream_t s, cdk_dek_t dek, + int use_mdc); +cdk_error_t cdk_stream_set_compress_flag (cdk_stream_t s, int algo, int level); +cdk_error_t cdk_stream_set_hash_flag (cdk_stream_t s, int algo); +cdk_error_t cdk_stream_set_text_flag (cdk_stream_t s, const char * lf); +cdk_error_t cdk_stream_kick_off (cdk_stream_t inp, cdk_stream_t out); +cdk_error_t cdk_stream_mmap (cdk_stream_t s, unsigned char **ret_buf, + size_t *ret_buflen); +cdk_error_t cdk_stream_mmap_part (cdk_stream_t s, off_t off, size_t len, + unsigned char **ret_buf, size_t *ret_buflen); + +/* Read from the stream but restore the file pointer after reading + the requested amount of bytes. */ +int cdk_stream_peek (cdk_stream_t inp, unsigned char *buf, size_t buflen); + +/* A wrapper around the various new_from_XXX functions. Because + the function does not support all combinations, the dedicated + functions should be preferred. */ +cdk_error_t cdk_keydb_new (cdk_keydb_hd_t *r_hd, int type, void *data, + size_t count); + +/* Create a new key db handle from a memory buffer. */ +cdk_error_t cdk_keydb_new_from_mem (cdk_keydb_hd_t *r_hd, int secret, + const void *data, size_t datlen); + +/* Create a new key db which uses an existing file. */ +cdk_error_t cdk_keydb_new_from_file (cdk_keydb_hd_t *r_hd, int secret, + const char *fname); + +/* Uses a stream as the key db input. For searching it is important + that the seek function is supported on the stream. Furthermore, + the stream is not closed in cdk_keydb_free(). The caller must do it. */ +cdk_error_t cdk_keydb_new_from_stream (cdk_keydb_hd_t *r_hd, int secret, + cdk_stream_t in); + +/* Check that a secret key with the given key ID is available. */ +cdk_error_t cdk_keydb_check_sk (cdk_keydb_hd_t hd, unsigned int *keyid); + +/* Prepare the key db search. */ +cdk_error_t cdk_keydb_search_start (cdk_keydb_hd_t hd, int type, void *desc); + +/* Return a key which matches a valid description given in + cdk_keydb_search_start(). */ +cdk_error_t cdk_keydb_search (cdk_keydb_hd_t hd, cdk_kbnode_t *ret_key); + +/* Release the key db handle and all its resources. */ +void cdk_keydb_free (cdk_keydb_hd_t hd); + +/* The following functions will try to find a key in the given key + db handle either by keyid, by fingerprint or by some pattern. */ +cdk_error_t cdk_keydb_get_bykeyid (cdk_keydb_hd_t hd, unsigned int *keyid, + cdk_kbnode_t *ret_pk); +cdk_error_t cdk_keydb_get_byfpr (cdk_keydb_hd_t hd, const unsigned char *fpr, + cdk_kbnode_t *ret_pk); +cdk_error_t cdk_keydb_get_bypattern (cdk_keydb_hd_t hd, const char *patt, + cdk_kbnode_t *ret_pk); + +/* These function, in contrast to most other key db functions, only + return the public or secret key packet without the additional + signatures and user IDs. */ +cdk_error_t cdk_keydb_get_pk (cdk_keydb_hd_t khd, unsigned int * keyid, + cdk_pubkey_t *ret_pk); +cdk_error_t cdk_keydb_get_sk (cdk_keydb_hd_t khd, unsigned int * keyid, + cdk_seckey_t *ret_sk); + +/* Try to read the next key block from the given input stream. + The key will be returned in @RET_KEY on success. */ +cdk_error_t cdk_keydb_get_keyblock (cdk_stream_t inp, cdk_kbnode_t * ret_key); + +/* Rebuild the key db index if possible. */ +cdk_error_t cdk_keydb_idx_rebuild (cdk_keydb_hd_t hd); + +/* Export one or more keys from the given key db handle into + the stream @OUT. The export is done by substring search and + uses the string list @REMUSR for the pattern. */ +cdk_error_t cdk_keydb_export (cdk_keydb_hd_t hd, cdk_stream_t out, + cdk_strlist_t remusr); + +/* Import the given key node @knode into the key db. */ +cdk_error_t cdk_keydb_import (cdk_keydb_hd_t hd, cdk_kbnode_t knode); + + +/* List or enumerate keys from a given key db handle. */ + +/* Start the key list process. Either use @PATT for a pattern search + or @FPATT for a list of pattern. */ +cdk_error_t cdk_listkey_start (cdk_listkey_t *r_ctx, cdk_keydb_hd_t db, + const char *patt, cdk_strlist_t fpatt); +void cdk_listkey_close (cdk_listkey_t ctx); + +/* Return the next key which matches the pattern. */ +cdk_error_t cdk_listkey_next (cdk_listkey_t ctx, cdk_kbnode_t *ret_key); + +cdk_kbnode_t cdk_kbnode_new (cdk_packet_t pkt); +cdk_error_t cdk_kbnode_read_from_mem (cdk_kbnode_t * ret_node, + const unsigned char * buf, + size_t buflen); +cdk_error_t cdk_kbnode_write_to_mem (cdk_kbnode_t node, + unsigned char * buf, size_t * r_nbytes); +cdk_error_t cdk_kbnode_write_to_mem_alloc (cdk_kbnode_t node, + unsigned char **r_buf, + size_t *r_buflen); + +void cdk_kbnode_release (cdk_kbnode_t node); +cdk_kbnode_t cdk_kbnode_walk (cdk_kbnode_t root, cdk_kbnode_t * ctx, int all); +cdk_packet_t cdk_kbnode_find_packet (cdk_kbnode_t node, int pkttype); +cdk_packet_t cdk_kbnode_get_packet (cdk_kbnode_t node); +cdk_kbnode_t cdk_kbnode_find (cdk_kbnode_t node, int pkttype); +cdk_kbnode_t cdk_kbnode_find_prev (cdk_kbnode_t root, cdk_kbnode_t node, + int pkttype); +cdk_kbnode_t cdk_kbnode_find_next (cdk_kbnode_t node, int pkttype); +cdk_error_t cdk_kbnode_hash (cdk_kbnode_t node, gcry_md_hd_t md, int is_v4, + int pkttype, int flags); + +/* Check each signature in the key node and return a summary of the + key status in @r_status. Values of cdk_key_flag_t are used. */ +cdk_error_t cdk_pk_check_sigs (cdk_kbnode_t knode, cdk_keydb_hd_t hd, + int *r_status); + +/* Check the self signature of the key to make sure it is valid. */ +cdk_error_t cdk_pk_check_self_sig (cdk_kbnode_t knode, int *r_status); + +/* Return a matching algorithm from the given public key list. + @PREFTYPE can be either sym-cipher/compress/digest. */ +int cdk_pklist_select_algo (cdk_keylist_t pkl, int preftype); + +/* Return 0 or 1 if the given key list is able to understand the + MDC feature. */ +int cdk_pklist_use_mdc (cdk_keylist_t pkl); +cdk_error_t cdk_pklist_build (cdk_keylist_t *ret_pkl, cdk_keydb_hd_t hd, + cdk_strlist_t remusr, int use); +void cdk_pklist_release (cdk_keylist_t pkl); + +/* Encrypt the given DEK key with the list of public keys given + in @PKL. The result will be written to the stream output @OUT. */ +cdk_error_t cdk_pklist_encrypt (cdk_keylist_t pkl, cdk_dek_t dek, + cdk_stream_t out); + +/* Secret key lists */ +cdk_error_t cdk_sklist_build (cdk_keylist_t * ret_skl, + cdk_keydb_hd_t db, cdk_ctx_t hd, + cdk_strlist_t locusr, + int unlock, unsigned int use); +void cdk_sklist_release (cdk_keylist_t skl); +cdk_error_t cdk_sklist_write (cdk_keylist_t skl, cdk_stream_t outp, + gcry_md_hd_t mdctx, + int sigclass, int sigver); +cdk_error_t cdk_sklist_write_onepass (cdk_keylist_t skl, cdk_stream_t outp, + int sigclass, int mdalgo); + +/* Encrypt the given stream @INP with the recipients given in @REMUSR. + If @REMUSR is NULL, symmetric encryption will be used. The output + will be written to @OUT. */ +cdk_error_t cdk_stream_encrypt (cdk_ctx_t hd, cdk_strlist_t remusr, + cdk_stream_t inp, cdk_stream_t out); + +/* Decrypt the @INP stream into @OUT. */ +cdk_error_t cdk_stream_decrypt (cdk_ctx_t hd, cdk_stream_t inp, + cdk_stream_t out); + +/* Same as the function above but it works on files. */ +cdk_error_t cdk_file_encrypt (cdk_ctx_t hd, cdk_strlist_t remusr, + const char *file, const char *output); +cdk_error_t cdk_file_decrypt (cdk_ctx_t hd, const char * file, + const char *output); + +/* Generic function to transform data. The mode can be either sign, + verify, encrypt, decrypt, import or export. The meanings of the + parameters are similar to the functions above. + @OUTBUF will contain the output and @OUTSIZE the length of it. */ +cdk_error_t cdk_data_transform (cdk_ctx_t hd, enum cdk_crypto_mode_t mode, + cdk_strlist_t locusr, cdk_strlist_t remusr, + const void * inbuf, size_t insize, + unsigned char ** outbuf, size_t * outsize, + int modval); + +/* Sign the stream @INP. Optionally, the output will be encrypted + if @REMUSR is not NULL and the @ENCRYPTFLAG is set. + The output will be written to @OUT. + @LOCUSR contains one ore more pattern for the secret key(s) to use. */ +cdk_error_t cdk_stream_sign (cdk_ctx_t hd, cdk_stream_t inp, cdk_stream_t out, + cdk_strlist_t locusr, cdk_strlist_t remusr, + int encryptflag, int sigmode); + +/* Same as the function above but it works on files. */ +cdk_error_t cdk_file_sign (cdk_ctx_t hd, cdk_strlist_t locusr, + cdk_strlist_t remusr, + const char *file, const char *output, + int sigmode, int encryptflag); + +cdk_error_t cdk_stream_verify (cdk_ctx_t hd, cdk_stream_t inp, + cdk_stream_t data, + cdk_stream_t out); + +/* Verify the given file @FILE. For a detached signature, @DATA_FILE + contains the actual file data and @FILE is only the signature. + If the @OUTPUT is not NULL, the plaintext will be written to this file. */ +cdk_error_t cdk_file_verify (cdk_ctx_t hd, const char *file, + const char *data_file, const char *output); + +int cdk_trustdb_get_validity (cdk_stream_t inp, cdk_pkt_userid_t id, + int *r_val); +int cdk_trustdb_get_ownertrust (cdk_stream_t inp, cdk_pubkey_t pk, + int *r_val, int *r_flags); + +void cdk_strlist_free (cdk_strlist_t sl); +cdk_strlist_t cdk_strlist_add (cdk_strlist_t * list, const char * string); +cdk_strlist_t cdk_strlist_next (cdk_strlist_t root, const char **r_str); +const char * cdk_check_version (const char * req_version); +/* UTF8 */ +char* cdk_utf8_encode (const char * string); +char* cdk_utf8_decode (const char * string, size_t length, int delim); + +/* Try to receive the key, which has the key ID @KEYID, from the + keyserver host @HOST and the port @PORT. */ +cdk_error_t cdk_keyserver_recv_key (const char *host, int port, + const unsigned char *keyid, int kid_type, + cdk_kbnode_t *r_key); + +cdk_error_t cdk_keygen_new (cdk_keygen_ctx_t * r_hd); +void cdk_keygen_free (cdk_keygen_ctx_t hd); + +/* Set the preferences of the given type for the new key. + @ARRAY is an array which list of algorithm IDs. */ +cdk_error_t cdk_keygen_set_prefs (cdk_keygen_ctx_t hd, + enum cdk_pref_type_t type, + const unsigned char * array, size_t n); +cdk_error_t cdk_keygen_set_algo_info (cdk_keygen_ctx_t hd, int type, + int usage, enum cdk_pubkey_algo_t algo, + unsigned int bits); +int cdk_keygen_set_keyserver_flags (cdk_keygen_ctx_t hd, int no_modify, + const char *pref_url); +int cdk_keygen_set_expire_date (cdk_keygen_ctx_t hd, int type, + long timestamp); + +/* Set the user ID specifc parts for the new key. + It is suggested to use a name in the form of + 'First Name' 'Last Name' */ +void cdk_keygen_set_name (cdk_keygen_ctx_t hd, const char * name); +void cdk_keygen_set_passphrase (cdk_keygen_ctx_t hd, const char * pass); +cdk_error_t cdk_keygen_start (cdk_keygen_ctx_t hd); +cdk_error_t cdk_keygen_save (cdk_keygen_ctx_t hd, + const char * pubf, + const char * secf); + +#ifdef __cplusplus +} +#endif + +#endif /* OPENCDK_H */ + diff --git a/src/daemon/https/opencdk/packet.h b/src/daemon/https/opencdk/packet.h new file mode 100644 index 00000000..03547ace --- /dev/null +++ b/src/daemon/https/opencdk/packet.h @@ -0,0 +1,40 @@ +/* packet.h + * Copyright (C) 2002, 2003, 2007 Timo Schulz + * + * This file is part of OpenCDK. + * + * OpenCDK 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. + * + * OpenCDK 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. + */ +#ifndef CDK_PACKET_H +#define CDK_PACKET_H + +struct cdk_kbnode_s +{ + struct cdk_kbnode_s *next; + cdk_packet_t pkt; + unsigned int is_deleted:1; + unsigned int is_cloned:1; +}; + +/*-- new-packet.c --*/ +void _cdk_free_mpibuf (size_t n, gcry_mpi_t *array); +void _cdk_free_userid (cdk_pkt_userid_t uid); +void _cdk_free_signature( cdk_pkt_signature_t sig ); +cdk_prefitem_t _cdk_copy_prefs( const cdk_prefitem_t prefs ); +int _cdk_copy_userid( cdk_pkt_userid_t *dst, cdk_pkt_userid_t src ); +int _cdk_copy_pubkey( cdk_pkt_pubkey_t* dst, cdk_pkt_pubkey_t src ); +int _cdk_copy_seckey( cdk_pkt_seckey_t* dst, cdk_pkt_seckey_t src ); +int _cdk_copy_pk_to_sk( cdk_pkt_pubkey_t pk, cdk_pkt_seckey_t sk ); +int _cdk_copy_signature( cdk_pkt_signature_t* dst, cdk_pkt_signature_t src ); +int _cdk_pubkey_compare( cdk_pkt_pubkey_t a, cdk_pkt_pubkey_t b ); + +#endif /* CDK_PACKET_H */ + diff --git a/src/daemon/https/opencdk/pubkey.c b/src/daemon/https/opencdk/pubkey.c new file mode 100644 index 00000000..0d24df12 --- /dev/null +++ b/src/daemon/https/opencdk/pubkey.c @@ -0,0 +1,1380 @@ +/* pubkey.c - Public key API + * Copyright (C) 2007 Free Software Foundation, Inc. + * Copyright (C) 2002, 2003, 2007 Timo Schulz + * + * This file is part of OpenCDK. + * + * OpenCDK 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. + * + * OpenCDK 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. + */ +#ifdef HAVE_CONFIG_H +#include +#endif +#include +#include + +#include "opencdk.h" +#include "main.h" +#include "packet.h" + + +/* Convert the given secret key into a gcrypt SEXP object. */ +static int +seckey_to_sexp (gcry_sexp_t * r_skey, cdk_seckey_t sk) +{ + gcry_sexp_t sexp = NULL; + gcry_mpi_t *mpk = NULL, *msk = NULL; + gcry_error_t err; + cdk_pubkey_t pk; + const char *fmt; + + if (!r_skey || !sk || !sk->pk) + return CDK_Inv_Value; + + pk = sk->pk; + mpk = pk->mpi; + msk = sk->mpi; + + *r_skey = NULL; + if (is_RSA (sk->pubkey_algo)) + { + fmt = "(private-key(openpgp-rsa(n%m)(e%m)(d%m)(p%m)(q%m)(u%m)))"; + err = gcry_sexp_build (&sexp, NULL, fmt, mpk[0], mpk[1], + msk[0], msk[1], msk[2], msk[3]); + } + else if (is_ELG (sk->pubkey_algo)) + { + fmt = "(private-key(openpgp-elg(p%m)(g%m)(y%m)(x%m)))"; + err = gcry_sexp_build (&sexp, NULL, fmt, mpk[0], mpk[1], + mpk[2], msk[0]); + } + else if (is_DSA (sk->pubkey_algo)) + { + fmt = "(private-key(openpgp-dsa(p%m)(q%m)(g%m)(y%m)(x%m)))"; + err = gcry_sexp_build (&sexp, NULL, fmt, mpk[0], mpk[1], mpk[2], + mpk[3], msk[0]); + } + else + return CDK_Inv_Algo; + if (err) + return map_gcry_error (err); + *r_skey = sexp; + return 0; +} + + +/* Convert the given public key to a gcrypt SEXP object. */ +static cdk_error_t +pubkey_to_sexp (gcry_sexp_t * r_key_sexp, cdk_pubkey_t pk) +{ + gcry_mpi_t *m; + gcry_error_t err; + const char *fmt = NULL; + cdk_error_t rc = 0; + + if (!r_key_sexp || !pk) + return CDK_Inv_Value; + + m = pk->mpi; + if (is_RSA (pk->pubkey_algo)) + { + fmt = "(public-key(openpgp-rsa(n%m)(e%m)))"; + err = gcry_sexp_build (r_key_sexp, NULL, fmt, m[0], m[1]); + if (err) + rc = map_gcry_error (err); + } + else if (is_ELG (pk->pubkey_algo)) + { + fmt = "(public-key(openpgp-elg(p%m)(g%m)(y%m)))"; + err = gcry_sexp_build (r_key_sexp, NULL, fmt, m[0], m[1], m[2]); + if (err) + rc = map_gcry_error (err); + } + else if (is_DSA (pk->pubkey_algo)) + { + fmt = "(public-key(openpgp-dsa(p%m)(q%m)(g%m)(y%m)))"; + err = gcry_sexp_build (r_key_sexp, NULL, fmt, m[0], m[1], m[2], m[3]); + if (err) + rc = map_gcry_error (err); + } + else + rc = CDK_Inv_Algo; + return rc; +} + + +static cdk_error_t +enckey_to_sexp (gcry_sexp_t * r_sexp, gcry_mpi_t esk) +{ + gcry_error_t err; + + if (!r_sexp || !esk) + return CDK_Inv_Value; + err = gcry_sexp_build (r_sexp, NULL, "%m", esk); + if (err) + return map_gcry_error (err); + return 0; +} + + +static cdk_error_t +digest_to_sexp (gcry_sexp_t * r_md_sexp, int digest_algo, + const byte * md, size_t mdlen) +{ + gcry_mpi_t m; + gcry_error_t err; + + if (!r_md_sexp || !md) + return CDK_Inv_Value; + + if (!mdlen) + mdlen = gcry_md_get_algo_dlen (digest_algo); + if (!mdlen) + return CDK_Inv_Algo; + + err = gcry_mpi_scan (&m, GCRYMPI_FMT_USG, md, mdlen, &mdlen); + if (err) + return map_gcry_error (err); + + err = gcry_sexp_build (r_md_sexp, NULL, "%m", m); + gcry_mpi_release (m); + if (err) + return map_gcry_error (err); + return 0; +} + + +static cdk_error_t +sexp_to_mpi (gcry_sexp_t sexp, const char *val, gcry_mpi_t * ret_buf) +{ + gcry_sexp_t list; + + if (!sexp || !val || !ret_buf) + return CDK_Inv_Value; + + list = gcry_sexp_find_token (sexp, val, 0); + if (!list) + return CDK_Inv_Value; + + *ret_buf = gcry_sexp_nth_mpi (list, 1, 0); + gcry_sexp_release (list); + if (!*ret_buf) + return CDK_Inv_Value; + return 0; +} + + +static cdk_error_t +sexp_to_sig (cdk_pkt_signature_t sig, gcry_sexp_t sexp) +{ + if (!sig || !sexp) + return CDK_Inv_Value; + + /* ElGamal signatures are not supported any longer. */ + if (is_ELG (sig->pubkey_algo)) + { + _cdk_log_debug ("sexp_to_sig: unsupported signature type (ElGamal)\n"); + return CDK_Not_Implemented; + } + + if (is_RSA (sig->pubkey_algo)) + return sexp_to_mpi (sexp, "s", &sig->mpi[0]); + else if (is_DSA (sig->pubkey_algo)) + { + cdk_error_t rc; + + rc = sexp_to_mpi (sexp, "r", &sig->mpi[0]); + if (!rc) + rc = sexp_to_mpi (sexp, "s", &sig->mpi[1]); + return rc; + } + return CDK_Inv_Algo; +} + + +static cdk_error_t +sig_to_sexp (gcry_sexp_t * r_sig_sexp, cdk_pkt_signature_t sig) +{ + gcry_error_t err; + cdk_error_t rc; + const char *fmt; + + if (!r_sig_sexp || !sig) + return CDK_Inv_Value; + if (is_ELG (sig->pubkey_algo)) + return CDK_Not_Implemented; + + rc = 0; + if (is_RSA (sig->pubkey_algo)) + { + fmt = "(sig-val(openpgp-rsa(s%m)))"; + err = gcry_sexp_build (r_sig_sexp, NULL, fmt, sig->mpi[0]); + if (err) + rc = map_gcry_error (err); + } + else if (is_DSA (sig->pubkey_algo)) + { + fmt = "(sig-val(openpgp-dsa(r%m)(s%m)))"; + err = gcry_sexp_build (r_sig_sexp, NULL, fmt, sig->mpi[0], sig->mpi[1]); + if (err) + rc = map_gcry_error (err); + } + else + rc = CDK_Inv_Algo; + return rc; +} + + +static cdk_error_t +sexp_to_pubenc (cdk_pkt_pubkey_enc_t enc, gcry_sexp_t sexp) +{ + if (!sexp || !enc) + return CDK_Inv_Value; + + if (is_RSA (enc->pubkey_algo)) + return sexp_to_mpi (sexp, "a", &enc->mpi[0]); + else if (is_ELG (enc->pubkey_algo)) + { + cdk_error_t rc = sexp_to_mpi (sexp, "a", &enc->mpi[0]); + if (!rc) + rc = sexp_to_mpi (sexp, "b", &enc->mpi[1]); + return rc; + } + return CDK_Inv_Algo; +} + + +static cdk_error_t +pubenc_to_sexp (gcry_sexp_t * r_sexp, cdk_pkt_pubkey_enc_t enc) +{ + gcry_sexp_t sexp = NULL; + gcry_error_t err; + const char *fmt; + + if (!r_sexp || !enc) + return CDK_Inv_Value; + + *r_sexp = NULL; + if (is_RSA (enc->pubkey_algo)) + { + fmt = "(enc-val(openpgp-rsa((a%m))))"; + err = gcry_sexp_build (&sexp, NULL, fmt, enc->mpi[0]); + } + else if (is_ELG (enc->pubkey_algo)) + { + fmt = "(enc-val(openpgp-elg((a%m)(b%m))))"; + err = gcry_sexp_build (&sexp, NULL, fmt, enc->mpi[0], enc->mpi[1]); + } + else + return CDK_Inv_Algo; + if (err) + return map_gcry_error (err); + *r_sexp = sexp; + return 0; +} + + +static int +is_unprotected (cdk_seckey_t sk) +{ + if (sk->is_protected && !sk->mpi[0]) + return 0; + return 1; +} + + +/** + * cdk_pk_encrypt: + * @pk: the public key + * @pke: the public key encrypted packet + * @esk: the actual session key + * + * Encrypt the session key in @esk and write its encrypted content + * into the @pke struct. + **/ +cdk_error_t +cdk_pk_encrypt (cdk_pubkey_t pk, cdk_pkt_pubkey_enc_t pke, gcry_mpi_t esk) +{ + gcry_sexp_t s_data = NULL, s_pkey = NULL, s_ciph = NULL; + gcry_error_t err; + cdk_error_t rc; + + if (!pk || !esk || !pke) + return CDK_Inv_Value; + + if (!KEY_CAN_ENCRYPT (pk->pubkey_algo)) + return CDK_Inv_Algo; + + rc = enckey_to_sexp (&s_data, esk); + if (!rc) + rc = pubkey_to_sexp (&s_pkey, pk); + if (!rc) + { + err = gcry_pk_encrypt (&s_ciph, s_data, s_pkey); + if (err) + return map_gcry_error (err); + } + if (!rc) + rc = sexp_to_pubenc (pke, s_ciph); + + gcry_sexp_release (s_data); + gcry_sexp_release (s_pkey); + gcry_sexp_release (s_ciph); + return rc; +} + + +/** + * cdk_pk_decrypt: + * @sk: the secret key + * @pke: public key encrypted packet + * @r_sk: the object to store the plain session key + * + * Decrypt the encrypted session key from @pke into @r_sk. + **/ +cdk_error_t +cdk_pk_decrypt (cdk_seckey_t sk, cdk_pkt_pubkey_enc_t pke, gcry_mpi_t * r_sk) +{ + gcry_sexp_t s_data = NULL, s_skey = NULL, s_plain = NULL; + cdk_error_t rc; + gcry_error_t err; + + if (!sk || !r_sk || !pke) + return CDK_Inv_Value; + + if (!is_unprotected (sk)) + return CDK_Inv_Mode; + + *r_sk = NULL; + rc = seckey_to_sexp (&s_skey, sk); + if (rc) + return rc; + + rc = pubenc_to_sexp (&s_data, pke); + if (rc) + { + gcry_sexp_release (s_skey); + return rc; + } + + err = gcry_pk_decrypt (&s_plain, s_data, s_skey); + if (err) + rc = map_gcry_error (err); + else + *r_sk = gcry_sexp_nth_mpi (s_plain, 0, 0); + + gcry_sexp_release (s_data); + gcry_sexp_release (s_skey); + gcry_sexp_release (s_plain); + return rc; +} + + +/** + * cdk_pk_sign: + * @sk: secret key + * @sig: signature + * @md: the message digest + * + * Sign the message digest from @md and write the result into @sig. + **/ +cdk_error_t +cdk_pk_sign (cdk_seckey_t sk, cdk_pkt_signature_t sig, const byte * md) +{ + gcry_sexp_t s_skey = NULL, s_sig = NULL, s_hash = NULL; + byte *encmd = NULL; + size_t enclen = 0; + int nbits; + cdk_error_t rc; + gcry_error_t err; + + if (!sk || !sk->pk || !sig || !md) + return CDK_Inv_Value; + + if (!is_unprotected (sk)) + return CDK_Inv_Mode; + + if (!KEY_CAN_SIGN (sig->pubkey_algo)) + return CDK_Inv_Algo; + + nbits = cdk_pk_get_nbits (sk->pk); + rc = _cdk_digest_encode_pkcs1 (&encmd, &enclen, sk->pk->pubkey_algo, md, + sig->digest_algo, nbits); + if (rc) + return rc; + + rc = seckey_to_sexp (&s_skey, sk); + if (!rc) + rc = digest_to_sexp (&s_hash, sig->digest_algo, encmd, enclen); + if (rc) + { + cdk_free (encmd); + gcry_sexp_release (s_skey); + return rc; + } + + err = gcry_pk_sign (&s_sig, s_hash, s_skey); + if (err) + rc = map_gcry_error (err); + else + { + rc = sexp_to_sig (sig, s_sig); + if (!rc) + { + sig->digest_start[0] = md[0]; + sig->digest_start[1] = md[1]; + } + } + + gcry_sexp_release (s_skey); + gcry_sexp_release (s_hash); + gcry_sexp_release (s_sig); + cdk_free (encmd); + return rc; +} + + +/** + * cdk_pk_verify: + * @pk: the public key + * @sig: signature + * @md: the message digest + * + * Verify the signature in @sig and compare it with the message digest in @md. + **/ +cdk_error_t +cdk_pk_verify (cdk_pubkey_t pk, cdk_pkt_signature_t sig, const byte * md) +{ + gcry_sexp_t s_pkey = NULL, s_sig = NULL, s_hash = NULL; + byte *encmd = NULL; + size_t enclen; + cdk_error_t rc; + + if (!pk || !sig || !md) + return CDK_Inv_Value; + + rc = pubkey_to_sexp (&s_pkey, pk); + if (rc) + return rc; + + rc = sig_to_sexp (&s_sig, sig); + if (rc) + goto leave; + + rc = _cdk_digest_encode_pkcs1 (&encmd, &enclen, pk->pubkey_algo, md, + sig->digest_algo, cdk_pk_get_nbits (pk)); + if (rc) + goto leave; + + rc = digest_to_sexp (&s_hash, sig->digest_algo, encmd, enclen); + if (rc) + goto leave; + + if (gcry_pk_verify (s_sig, s_hash, s_pkey)) + rc = CDK_Bad_Sig; + +leave: + gcry_sexp_release (s_sig); + gcry_sexp_release (s_hash); + gcry_sexp_release (s_pkey); + cdk_free (encmd); + return rc; +} + + +/** + * cdk_pk_get_nbits: + * @pk: the public key + * + * Return the length of the public key in bits. + * The meaning of length is actually the size of the 'prime' + * object in the key. For RSA keys the modulus, for ElG/DSA + * the size of the public prime. + **/ +int +cdk_pk_get_nbits (cdk_pubkey_t pk) +{ + if (!pk || !pk->mpi[0]) + return 0; + return gcry_mpi_get_nbits (pk->mpi[0]); +} + + +/** + * cdk_pk_get_npkey: + * @algo: The public key algorithm. + * + * Return the number of multiprecison integer forming an public + * key with the given algorithm. + */ +int +cdk_pk_get_npkey (int algo) +{ + size_t bytes; + + if (algo == 16) + algo = 20; /* FIXME: libgcrypt returns 0 for 16 */ + if (gcry_pk_algo_info (algo, GCRYCTL_GET_ALGO_NPKEY, NULL, &bytes)) + return 0; + return bytes; +} + + +/** + * cdk_pk_get_nskey: + * @algo: the public key algorithm + * + * Return the number of multiprecision integers forming an + * secret key with the given algorithm. + **/ +int +cdk_pk_get_nskey (int algo) +{ + size_t bytes; + + if (gcry_pk_algo_info (algo, GCRYCTL_GET_ALGO_NSKEY, NULL, &bytes)) + return 0; + bytes -= cdk_pk_get_npkey (algo); + return bytes; +} + + +/** + * cdk_pk_get_nbits: + * @algo: the public key algorithm + * + * Return the number of MPIs a signature consists of. + **/ +int +cdk_pk_get_nsig (int algo) +{ + size_t bytes; + + if (gcry_pk_algo_info (algo, GCRYCTL_GET_ALGO_NSIGN, NULL, &bytes)) + return 0; + return bytes; +} + + +/** + * cdk_pk_get_nenc: + * @algo: the public key algorithm + * + * Return the number of MPI's the encrypted data consists of. + **/ +int +cdk_pk_get_nenc (int algo) +{ + size_t bytes; + + if (gcry_pk_algo_info (algo, GCRYCTL_GET_ALGO_NENCR, NULL, &bytes)) + return 0; + return bytes; +} + + +int +_cdk_pk_algo_usage (int algo) +{ + int usage; + + /* The ElGamal sign+encrypt algorithm is not supported any longer. */ + switch (algo) + { + case CDK_PK_RSA: + usage = CDK_KEY_USG_SIGN | CDK_KEY_USG_ENCR; + break; + case CDK_PK_RSA_E: + usage = CDK_KEY_USG_ENCR; + break; + case CDK_PK_RSA_S: + usage = CDK_KEY_USG_SIGN; + break; + case CDK_PK_ELG_E: + usage = CDK_KEY_USG_ENCR; + break; + case CDK_PK_DSA: + usage = CDK_KEY_USG_SIGN; + break; + default: + usage = 0; + } + return usage; +} + + +static cdk_error_t +mpi_to_buffer (gcry_mpi_t a, byte * buf, size_t buflen, + size_t * r_nwritten, size_t * r_nbits) +{ + size_t nbits; + + if (!a || !buf || !r_nwritten) + return CDK_Inv_Value; + + nbits = gcry_mpi_get_nbits (a); + if (r_nbits) + *r_nbits = nbits; + if ((nbits + 7) / 8 + 2 > buflen) + return CDK_Too_Short; + *r_nwritten = (nbits + 7) / 8 + 2; + if (gcry_mpi_print (GCRYMPI_FMT_PGP, buf, buflen, r_nwritten, a)) + return CDK_Wrong_Format; + return 0; +} + + +/** + * cdk_pk_get_mpi: + * @pk: public key + * @idx: index of the MPI to retrieve + * @buf: buffer to hold the raw data + * @r_nwritten: output how large the raw data is + * @r_nbits: size of the MPI in bits. + * + * Return the MPI with the given index of the public key. + **/ +cdk_error_t +cdk_pk_get_mpi (cdk_pubkey_t pk, size_t idx, + byte * buf, size_t buflen, size_t * r_nwritten, + size_t * r_nbits) +{ + if (!pk || !r_nwritten) + return CDK_Inv_Value; + if (idx > cdk_pk_get_npkey (pk->pubkey_algo)) + return CDK_Inv_Value; + return mpi_to_buffer (pk->mpi[idx], buf, buflen, r_nwritten, r_nbits); +} + + +/** + * cdk_sk_get_mpi: + * @sk: secret key + * @idx: index of the MPI to retrieve + * @buf: buffer to hold the raw data + * @r_nwritten: output length of the raw data + * @r_nbits: length of the MPI data in bits. + * + * Return the MPI of the given secret key with the + * index @idx. It is important to check if the key + * is protected and thus no real MPI data will be returned then. + **/ +cdk_error_t +cdk_sk_get_mpi (cdk_pkt_seckey_t sk, size_t idx, + byte * buf, size_t buflen, size_t * r_nwritten, + size_t * r_nbits) +{ + if (!sk || !r_nwritten) + return CDK_Inv_Value; + if (idx > cdk_pk_get_nskey (sk->pubkey_algo)) + return CDK_Inv_Value; + return mpi_to_buffer (sk->mpi[idx], buf, buflen, r_nwritten, r_nbits); +} + + +static u16 +checksum_mpi (gcry_mpi_t m) +{ + byte buf[MAX_MPI_BYTES + 2]; + size_t nread; + int i; + u16 chksum = 0; + + if (!m) + return 0; + if (gcry_mpi_print (GCRYMPI_FMT_PGP, buf, DIM (buf), &nread, m)) + return 0; + for (i = 0; i < nread; i++) + chksum += buf[i]; + return chksum; +} + + +/** + * cdk_sk_unprotect: + * @sk: the secret key + * @pw: the passphrase + * + * Unprotect the given secret key with the passphrase. + **/ +cdk_error_t +cdk_sk_unprotect (cdk_pkt_seckey_t sk, const char *pw) +{ + gcry_cipher_hd_t hd; + cdk_dek_t dek = NULL; + byte *data = NULL; + u16 chksum = 0; + size_t ndata, nbits, nbytes; + int i, dlen, pos = 0, nskey; + cdk_error_t rc; + gcry_error_t err; + + if (!sk) + return CDK_Inv_Value; + + nskey = cdk_pk_get_nskey (sk->pubkey_algo); + if (!sk->is_protected) + { + chksum = 0; + for (i = 0; i < nskey; i++) + chksum += checksum_mpi (sk->mpi[i]); + if (chksum != sk->csum) + return CDK_Chksum_Error; + } + + rc = cdk_dek_from_passphrase (&dek, sk->protect.algo, + sk->protect.s2k, 0, pw); + if (rc) + return rc; + err = gcry_cipher_open (&hd, sk->protect.algo, GCRY_CIPHER_MODE_CFB, + GCRY_CIPHER_ENABLE_SYNC); + if (!err) + err = gcry_cipher_setiv (hd, sk->protect.iv, sk->protect.ivlen); + if (!err) + err = gcry_cipher_setkey (hd, dek->key, dek->keylen); + if (err) + { + cdk_free (dek); + return map_gcry_error (err); + } + cdk_dek_free (dek); + chksum = 0; + if (sk->version == 4) + { + ndata = sk->enclen; + data = cdk_salloc (ndata, 1); + if (!data) + return CDK_Out_Of_Core; + gcry_cipher_decrypt (hd, data, ndata, sk->encdata, ndata); + if (sk->protect.sha1chk) + { + /* This is the new SHA1 checksum method to detect tampering + with the key as used by the Klima/Rosa attack */ + sk->csum = 0; + chksum = 1; + dlen = gcry_md_get_algo_dlen (GCRY_MD_SHA1); + if (ndata < dlen) + { + cdk_free (data); + return CDK_Inv_Packet; + } + else + { + byte mdcheck[20]; + + gcry_md_hash_buffer (GCRY_MD_SHA1, mdcheck, data, ndata - dlen); + if (!memcmp (mdcheck, data + ndata - dlen, dlen)) + chksum = 0; /* Digest does match */ + } + } + else + { + for (i = 0; i < ndata - 2; i++) + chksum += data[i]; + sk->csum = data[ndata - 2] << 8 | data[ndata - 1]; + } + if (sk->csum == chksum) + { + for (i = 0; i < nskey; i++) + { + nbits = data[pos] << 8 | data[pos + 1]; + + if (gcry_mpi_scan (&sk->mpi[i], GCRYMPI_FMT_PGP, data, + (nbits + 7) / 8 + 2, &nbytes)) + { + wipemem (data, sk->enclen); + cdk_free (data); + return CDK_Wrong_Format; + } + gcry_mpi_set_flag (sk->mpi[i], GCRYMPI_FLAG_SECURE); + pos += (nbits + 7) / 8 + 2; + } + } + wipemem (data, sk->enclen); + cdk_free (data); + } + else + { + byte buf[MAX_MPI_BYTES + 2]; + + chksum = 0; + for (i = 0; i < nskey; i++) + { + gcry_cipher_sync (hd); + gcry_mpi_print (GCRYMPI_FMT_PGP, buf, DIM (buf), + &nbytes, sk->mpi[i]); + gcry_cipher_decrypt (hd, buf + 2, nbytes - 2, NULL, 0); + gcry_mpi_release (sk->mpi[i]); + if (gcry_mpi_scan (&sk->mpi[i], GCRYMPI_FMT_PGP, + buf, nbytes, &nbytes)) + return CDK_Wrong_Format; + chksum += checksum_mpi (sk->mpi[i]); + } + } + gcry_cipher_close (hd); + if (chksum != sk->csum) + return CDK_Chksum_Error; + sk->is_protected = 0; + return 0; +} + + +/** + * cdk_sk_protect: + * @sk: the secret key + * @pw: the passphrase to use + * + * Protect the given secret key with a passphrase. + **/ +cdk_error_t +cdk_sk_protect (cdk_pkt_seckey_t sk, const char *pw) +{ + gcry_cipher_hd_t hd = NULL; + cdk_dek_t dek = NULL; + cdk_s2k_t s2k; + byte *p = NULL, buf[MAX_MPI_BYTES + 2]; + size_t enclen = 0, nskey, i, nbytes; + size_t dlen = gcry_md_get_algo_dlen (GCRY_MD_SHA1); + gcry_error_t err; + cdk_error_t rc; + + nskey = cdk_pk_get_nskey (sk->pubkey_algo); + if (!nskey) + return CDK_Inv_Algo; + + rc = cdk_s2k_new (&s2k, CDK_S2K_ITERSALTED, GCRY_MD_SHA256, NULL); + if (!rc) + rc = cdk_dek_from_passphrase (&dek, GCRY_CIPHER_AES, s2k, 1, pw); + if (rc) + { + cdk_s2k_free (s2k); + return rc; + } + + for (i = 0; i < nskey; i++) + { + enclen += 2; + enclen += (gcry_mpi_get_nbits (sk->mpi[i]) + 7) / 8; + } + p = sk->encdata = cdk_calloc (1, enclen + dlen + 1); + if (!p) + { + cdk_s2k_free (s2k); + return CDK_Out_Of_Core; + } + + enclen = 0; + for (i = 0; i < nskey; i++) + { + if (gcry_mpi_print (GCRYMPI_FMT_PGP, buf, + DIM (buf), &nbytes, sk->mpi[i])) + { + cdk_free (p); + cdk_s2k_free (s2k); + return CDK_Wrong_Format; + } + memcpy (p + enclen, buf, nbytes); + enclen += nbytes; + } + + enclen += dlen; + sk->enclen = enclen; + sk->protect.s2k = s2k; + sk->protect.algo = GCRY_CIPHER_AES; + sk->protect.ivlen = gcry_cipher_get_algo_blklen (sk->protect.algo); + gcry_randomize (sk->protect.iv, sk->protect.ivlen, GCRY_STRONG_RANDOM); + err = gcry_cipher_open (&hd, sk->protect.algo, GCRY_CIPHER_MODE_CFB, + GCRY_CIPHER_ENABLE_SYNC); + if (err) + { + cdk_dek_free (dek); + rc = map_gcry_error (err); + goto leave; + } + + err = gcry_cipher_setkey (hd, dek->key, dek->keylen); + if (!err) + err = gcry_cipher_setiv (hd, sk->protect.iv, sk->protect.ivlen); + cdk_dek_free (dek); + if (err) + { + rc = map_gcry_error (err); + goto leave; + } + + sk->protect.sha1chk = 1; + sk->is_protected = 1; + sk->csum = 0; + + gcry_md_hash_buffer (GCRY_MD_SHA1, buf, p, enclen - dlen); + memcpy (p + enclen - dlen, buf, dlen); + gcry_cipher_encrypt (hd, p, enclen, NULL, 0); + + /* FIXME: We should release all MPI's and set the elements to NULL. */ + +leave: + gcry_cipher_close (hd); + return rc; +} + + +/** + * cdk_pk_from_secret_key: + * @sk: the secret key + * @ret_pk: the new public key + * + * Create a new public key from a secret key. + **/ +cdk_error_t +cdk_pk_from_secret_key (cdk_pkt_seckey_t sk, cdk_pubkey_t * ret_pk) +{ + if (!sk) + return CDK_Inv_Value; + return _cdk_copy_pubkey (ret_pk, sk->pk); +} + + +#if 0 /* FIXME: Code is not finished yet. */ +cdk_error_t +cdk_pk_revoke_cert_create (cdk_pkt_seckey_t sk, int code, const char *inf, + char **ret_revcert) +{ + gcry_md_hd_t md; + cdk_subpkt_t node; + cdk_pkt_signature_t sig; + char *p = NULL, *dat; + gcry_error_t err; + cdk_error_t rc = 0; + size_t n; + + if (!sk || !ret_revcert) + return CDK_Inv_Value; + if (code < 0 || code > 3) + return CDK_Inv_Value; + + sig = cdk_calloc (1, sizeof *sig); + if (!sig) + return CDK_Out_Of_Core; + _cdk_sig_create (sk->pk, sig); + n = 1; + if (inf) + { + n += strlen (p); + p = cdk_utf8_encode (inf); + } + dat = cdk_calloc (1, n + 1); + if (!dat) + { + _cdk_free_signature (sig); + return CDK_Out_Of_Core; + } + dat[0] = code; + if (inf) + memcpy (dat + 1, p, strlen (p)); + cdk_free (p); + + node = cdk_subpkt_new (n); + if (node) + { + cdk_subpkt_init (node, CDK_SIGSUBPKT_REVOC_REASON, dat, n); + cdk_subpkt_add (sig->hashed, node); + } + cdk_free (dat); + + err = gcry_md_open (&md, GCRY_MD_SHA1, 0); + if (err) + rc = map_gcry_error (err); + else + _cdk_hash_pubkey (sk->pk, md, 0); + _cdk_free_signature (sig); + + return rc; +} +#endif + +int +_cdk_sk_get_csum (cdk_pkt_seckey_t sk) +{ + u16 csum = 0, i; + + if (!sk) + return 0; + for (i = 0; i < cdk_pk_get_nskey (sk->pubkey_algo); i++) + csum += checksum_mpi (sk->mpi[i]); + return csum; +} + + +/** + * cdk_pk_get_fingerprint: + * @pk: the public key + * @fpr: the buffer to hold the fingerprint + * + * Return the fingerprint of the given public key. + * The buffer must be at least 20 octets. + * This function should be considered deprecated and + * the new cdk_pk_to_fingerprint() should be used whenever + * possible to avoid overflows. + **/ +cdk_error_t +cdk_pk_get_fingerprint (cdk_pubkey_t pk, byte * fpr) +{ + gcry_md_hd_t hd; + int md_algo; + int dlen = 0; + gcry_error_t err; + + if (!pk || !fpr) + return CDK_Inv_Value; + + if (pk->version < 4 && is_RSA (pk->pubkey_algo)) + md_algo = GCRY_MD_MD5; /* special */ + else + md_algo = GCRY_MD_SHA1; + dlen = gcry_md_get_algo_dlen (md_algo); + err = gcry_md_open (&hd, md_algo, 0); + if (err) + return map_gcry_error (err); + _cdk_hash_pubkey (pk, hd, 1); + gcry_md_final (hd); + memcpy (fpr, gcry_md_read (hd, md_algo), dlen); + gcry_md_close (hd); + if (dlen == 16) + memset (fpr + 16, 0, 4); + return 0; +} + + +/** + * cdk_pk_to_fingerprint: + * @pk: the public key + * @fprbuf: buffer to save the fingerprint + * @fprbuflen: buffer size + * @r_nout: actual length of the fingerprint. + * + * Calculate a fingerprint of the given key and + * return it in the given byte array. + **/ +cdk_error_t +cdk_pk_to_fingerprint (cdk_pubkey_t pk, + byte * fprbuf, size_t fprbuflen, size_t * r_nout) +{ + size_t key_fprlen; + cdk_error_t err; + + if (!pk) + return CDK_Inv_Value; + + if (pk->version < 4) + key_fprlen = 16; + else + key_fprlen = 20; + + /* Only return the required buffer size for the fingerprint. */ + if (!fprbuf && !fprbuflen && r_nout) + { + *r_nout = key_fprlen; + return 0; + } + + if (!fprbuf || key_fprlen > fprbuflen) + return CDK_Too_Short; + + err = cdk_pk_get_fingerprint (pk, fprbuf); + if (r_nout) + *r_nout = key_fprlen; + + return err; +} + + +/** + * cdk_pk_fingerprint_get_keyid: + * @fpr: the key fingerprint + * @fprlen: the length of the fingerprint + * + * Derive the key ID from the key fingerprint. + * For version 3 keys, this is not working. + **/ +u32 +cdk_pk_fingerprint_get_keyid (const byte * fpr, size_t fprlen, u32 * keyid) +{ + u32 lowbits = 0; + + /* In this case we say the key is a V3 RSA key and we can't + use the fingerprint to get the keyid. */ + if (fpr && fprlen == 16) + { + keyid[0] = 0; + keyid[1] = 0; + return 0; + } + else if (keyid && fpr) + { + keyid[0] = _cdk_buftou32 (fpr + 12); + keyid[1] = _cdk_buftou32 (fpr + 16); + lowbits = keyid[1]; + } + else if (fpr) + lowbits = _cdk_buftou32 (fpr + 16); + return lowbits; +} + + +/** + * cdk_pk_get_keyid: + * @pk: the public key + * @keyid: buffer to store the key ID + * + * Calculate the key ID of the given public key. + **/ +u32 +cdk_pk_get_keyid (cdk_pubkey_t pk, u32 * keyid) +{ + u32 lowbits = 0; + byte buf[24]; + + if (pk && (!pk->keyid[0] || !pk->keyid[1])) + { + if (pk->version < 4 && is_RSA (pk->pubkey_algo)) + { + byte p[MAX_MPI_BYTES]; + size_t n; + + gcry_mpi_print (GCRYMPI_FMT_USG, p, MAX_MPI_BYTES, &n, pk->mpi[0]); + pk->keyid[0] = + p[n - 8] << 24 | p[n - 7] << 16 | p[n - 6] << 8 | p[n - 5]; + pk->keyid[1] = + p[n - 4] << 24 | p[n - 3] << 16 | p[n - 2] << 8 | p[n - 1]; + } + else if (pk->version == 4) + { + cdk_pk_get_fingerprint (pk, buf); + pk->keyid[0] = _cdk_buftou32 (buf + 12); + pk->keyid[1] = _cdk_buftou32 (buf + 16); + } + } + lowbits = pk ? pk->keyid[1] : 0; + if (keyid && pk) + { + keyid[0] = pk->keyid[0]; + keyid[1] = pk->keyid[1]; + } + + return lowbits; +} + + +/** + * cdk_sk_get_keyid: + * @sk: the secret key + * @keyid: buffer to hold the key ID + * + * Calculate the key ID of the secret key, actually the public key. + **/ +u32 +cdk_sk_get_keyid (cdk_pkt_seckey_t sk, u32 * keyid) +{ + u32 lowbits = 0; + + if (sk && sk->pk) + { + lowbits = cdk_pk_get_keyid (sk->pk, keyid); + sk->keyid[0] = sk->pk->keyid[0]; + sk->keyid[1] = sk->pk->keyid[1]; + } + + return lowbits; +} + + +/** + * cdk_sig_get_keyid: + * @sig: the signature + * @keyid: buffer to hold the key ID + * + * Retrieve the key ID from the given signature. + **/ +u32 +cdk_sig_get_keyid (cdk_pkt_signature_t sig, u32 * keyid) +{ + u32 lowbits = sig ? sig->keyid[1] : 0; + + if (keyid && sig) + { + keyid[0] = sig->keyid[0]; + keyid[1] = sig->keyid[1]; + } + return lowbits; +} + + +/* Return the key ID from the given packet. + If this is not possible, 0 is returned */ +u32 +_cdk_pkt_get_keyid (cdk_packet_t pkt, u32 * keyid) +{ + u32 lowbits; + + if (!pkt) + return 0; + + switch (pkt->pkttype) + { + case CDK_PKT_PUBLIC_KEY: + case CDK_PKT_PUBLIC_SUBKEY: + lowbits = cdk_pk_get_keyid (pkt->pkt.public_key, keyid); + break; + + case CDK_PKT_SECRET_KEY: + case CDK_PKT_SECRET_SUBKEY: + lowbits = cdk_sk_get_keyid (pkt->pkt.secret_key, keyid); + break; + + case CDK_PKT_SIGNATURE: + lowbits = cdk_sig_get_keyid (pkt->pkt.signature, keyid); + break; + + default: + lowbits = 0; + break; + } + + return lowbits; +} + + +/* Get the fingerprint of the packet if possible. */ +int +_cdk_pkt_get_fingerprint (cdk_packet_t pkt, byte * fpr) +{ + if (!pkt || !fpr) + return CDK_Inv_Value; + + switch (pkt->pkttype) + { + case CDK_PKT_PUBLIC_KEY: + case CDK_PKT_PUBLIC_SUBKEY: + return cdk_pk_get_fingerprint (pkt->pkt.public_key, fpr); + + case CDK_PKT_SECRET_KEY: + case CDK_PKT_SECRET_SUBKEY: + return cdk_pk_get_fingerprint (pkt->pkt.secret_key->pk, fpr); + + default: + return CDK_Inv_Mode; + } + return 0; +} + + +/** + * cdk_pubkey_to_sexp: + * @pk: the public key + * @sexp: where to store the S-expression + * @len: the length of sexp + * + * Convert a public key to an S-expression. sexp is allocated by this + * function, but you have to cdk_free() it yourself. The S-expression + * is stored in canonical format as used by libgcrypt + * (GCRYSEXP_FMT_CANON). + **/ +cdk_error_t +cdk_pubkey_to_sexp (cdk_pubkey_t pk, char **sexp, size_t * len) +{ + char *buf; + size_t sexp_len; + gcry_sexp_t pk_sexp; + cdk_error_t rc; + + if (!pk || !sexp) + return CDK_Inv_Value; + + rc = pubkey_to_sexp (&pk_sexp, pk); + if (rc) + return rc; + + sexp_len = gcry_sexp_sprint (pk_sexp, GCRYSEXP_FMT_CANON, NULL, 0); + if (!sexp_len) + return CDK_Wrong_Format; + + buf = (char *) cdk_malloc (sexp_len); + if (!buf) + { + gcry_sexp_release (pk_sexp); + return CDK_Out_Of_Core; + } + + sexp_len = gcry_sexp_sprint (pk_sexp, GCRYSEXP_FMT_CANON, buf, sexp_len); + gcry_sexp_release (pk_sexp); + if (!sexp_len) + { + cdk_free (buf); + return CDK_Wrong_Format; + } + + if (len) + *len = sexp_len; + *sexp = buf; + return CDK_Success; +} + + +/** + * cdk_seckey_to_sexp: + * @sk: the secret key + * @sexp: where to store the S-expression + * @len: the length of sexp + * + * Convert a public key to an S-expression. sexp is allocated by this + * function, but you have to cdk_free() it yourself. The S-expression + * is stored in canonical format as used by libgcrypt + * (GCRYSEXP_FMT_CANON). + **/ +cdk_error_t +cdk_seckey_to_sexp (cdk_pkt_seckey_t sk, char **sexp, size_t * len) +{ + char *buf; + size_t sexp_len; + gcry_sexp_t sk_sexp; + cdk_error_t rc; + + if (!sk || !sexp) + return CDK_Inv_Value; + + rc = seckey_to_sexp (&sk_sexp, sk); + if (rc) + return rc; + + sexp_len = gcry_sexp_sprint (sk_sexp, GCRYSEXP_FMT_CANON, NULL, 0); + if (!sexp_len) + return CDK_Wrong_Format; + + buf = (char *) cdk_malloc (sexp_len); + if (!buf) + { + gcry_sexp_release (sk_sexp); + return CDK_Out_Of_Core; + } + + sexp_len = gcry_sexp_sprint (sk_sexp, GCRYSEXP_FMT_CANON, buf, sexp_len); + gcry_sexp_release (sk_sexp); + if (!sexp_len) + { + cdk_free (buf); + return CDK_Wrong_Format; + } + + if (len) + *len = sexp_len; + *sexp = buf; + + return CDK_Success; +} diff --git a/src/daemon/https/opencdk/read-packet.c b/src/daemon/https/opencdk/read-packet.c new file mode 100644 index 00000000..7c7a91af --- /dev/null +++ b/src/daemon/https/opencdk/read-packet.c @@ -0,0 +1,1179 @@ +/* read-packet.c - Read OpenPGP packets + * Copyright (C) 2001, 2002, 2003, 2007 Timo Schulz + * + * This file is part of OpenCDK. + * + * OpenCDK 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. + * + * OpenCDK 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. + */ +#ifdef HAVE_CONFIG_H +#include +#endif +#include +#include +#include +#include + +#include "opencdk.h" +#include "main.h" +#include "packet.h" +#include "types.h" + + +/* The version of the MDC packet considering the lastest OpenPGP draft. */ +#define MDC_PKT_VER 1 + +static int +stream_read (cdk_stream_t s, void *buf, size_t buflen, size_t * r_nread) +{ + *r_nread = cdk_stream_read (s, buf, buflen); + return *r_nread > 0 ? 0 : _cdk_stream_get_errno (s); +} + + +/* Try to read 4 octets from the stream. */ +static u32 +read_32 (cdk_stream_t s) +{ + byte buf[4]; + size_t nread; + + assert (s != NULL); + + stream_read (s, buf, 4, &nread); + if (nread != 4) + return (u32) - 1; + return buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3]; +} + + +/* Try to read 2 octets from a stream. */ +static u16 +read_16 (cdk_stream_t s) +{ + byte buf[2]; + size_t nread; + + assert (s != NULL); + + stream_read (s, buf, 2, &nread); + if (nread != 2) + return (u16) - 1; + return buf[0] << 8 | buf[1]; +} + + +static int +read_s2k (cdk_stream_t inp, cdk_s2k_t s2k) +{ + size_t nread; + + if (!inp || !s2k) + return CDK_Inv_Value; + + if (DEBUG_PKT) + _cdk_log_debug ("read_s2k:\n"); + + s2k->mode = cdk_stream_getc (inp); + if (cdk_stream_eof (inp)) + return CDK_Inv_Packet; + if (s2k->mode != CDK_S2K_SIMPLE && s2k->mode != CDK_S2K_SALTED && + s2k->mode != CDK_S2K_ITERSALTED) + return CDK_Inv_Packet; + s2k->hash_algo = cdk_stream_getc (inp); + if (s2k->mode == CDK_S2K_SIMPLE) /* No additional elements. */ + memset (s2k->salt, 0, sizeof (s2k->salt)); + else if (s2k->mode == CDK_S2K_SALTED || s2k->mode == CDK_S2K_ITERSALTED) + { + if (stream_read (inp, s2k->salt, DIM (s2k->salt), &nread)) + return CDK_Inv_Packet; + if (nread != DIM (s2k->salt)) + return CDK_Inv_Packet; + if (s2k->mode == CDK_S2K_ITERSALTED) + { + s2k->count = cdk_stream_getc (inp); + if (cdk_stream_eof (inp)) + return CDK_Inv_Packet; + } + } + else + return CDK_Inv_Mode; + return 0; +} + + +static cdk_error_t +read_mpi (cdk_stream_t inp, gcry_mpi_t * ret_m, int secure) +{ + gcry_mpi_t m; + gcry_error_t err; + byte buf[MAX_MPI_BYTES + 2]; + size_t nread, nbits; + cdk_error_t rc; + + if (!inp || !ret_m) + return CDK_Inv_Value; + + *ret_m = NULL; + nbits = read_16 (inp); + nread = (nbits + 7) / 8; + + if (nbits > MAX_MPI_BITS || nbits == 0) + { + _cdk_log_debug ("read_mpi: too large %d bits\n", nbits); + return CDK_MPI_Error; /* Sanity check */ + } + + rc = stream_read (inp, buf + 2, nread, &nread); + if (!rc && nread != ((nbits + 7) / 8)) + { + _cdk_log_debug ("read_mpi: too short %d < %d\n", nread, + (nbits + 7) / 8); + return CDK_MPI_Error; + } + + buf[0] = nbits >> 8; + buf[1] = nbits >> 0; + err = gcry_mpi_scan (&m, GCRYMPI_FMT_PGP, buf, nread + 2, &nread); + if (err) + return map_gcry_error (err); + if (secure) + gcry_mpi_set_flag (m, GCRYMPI_FLAG_SECURE); + *ret_m = m; + return rc; +} + + +/* Read the encoded packet length directly from the file + object INP and return it. Reset RET_PARTIAL if this is + the last packet in block mode. */ +size_t +_cdk_pkt_read_len (FILE * inp, size_t * ret_partial) +{ + int c1, c2; + size_t pktlen; + + c1 = fgetc (inp); + if (c1 == EOF) + return (size_t) EOF; + if (c1 < 224 || c1 == 255) + *ret_partial = 0; /* End of partial data */ + if (c1 < 192) + pktlen = c1; + else if (c1 >= 192 && c1 <= 223) + { + c2 = fgetc (inp); + if (c2 == EOF) + return (size_t) EOF; + pktlen = ((c1 - 192) << 8) + c2 + 192; + } + else if (c1 == 255) + { + pktlen = fgetc (inp) << 24; + pktlen |= fgetc (inp) << 16; + pktlen |= fgetc (inp) << 8; + pktlen |= fgetc (inp) << 0; + } + else + pktlen = 1 << (c1 & 0x1f); + return pktlen; +} + + +static cdk_error_t +read_encrypted (cdk_stream_t inp, size_t pktlen, cdk_pkt_encrypted_t enc, + int is_partial, int is_mdc) +{ + if (!inp || !enc) + return CDK_Inv_Value; + + if (DEBUG_PKT) + _cdk_log_debug ("read_encrypted: %d octets\n", pktlen); + + if (is_mdc) + { + int version = cdk_stream_getc (inp); + if (version != MDC_PKT_VER) + return CDK_Inv_Packet; + enc->mdc_method = CDK_MD_SHA1; + pktlen--; + } + /* The packet must at least contain blocksize + 2 octets. */ + if (pktlen < 10) + return CDK_Inv_Packet; + if (is_partial) + _cdk_stream_set_blockmode (inp, pktlen); + enc->len = pktlen; + enc->buf = inp; + return 0; +} + + +static cdk_error_t +read_symkey_enc (cdk_stream_t inp, size_t pktlen, cdk_pkt_symkey_enc_t ske) +{ + cdk_s2k_t s2k; + size_t minlen; + size_t nread, nleft; + + if (!inp || !ske) + return CDK_Inv_Value; + + if (DEBUG_PKT) + _cdk_log_debug ("read_symkey_enc: %d octets\n", pktlen); + + ske->version = cdk_stream_getc (inp); + if (ske->version != 4 || cdk_stream_eof (inp)) + return CDK_Inv_Packet; + + s2k = ske->s2k = cdk_calloc (1, sizeof *ske->s2k); + if (!ske->s2k) + return CDK_Out_Of_Core; + + ske->cipher_algo = cdk_stream_getc (inp); + s2k->mode = cdk_stream_getc (inp); + switch (s2k->mode) + { + case CDK_S2K_SIMPLE: + minlen = 0; + break; + case CDK_S2K_SALTED: + minlen = 8; + break; + case CDK_S2K_ITERSALTED: + minlen = 9; + break; + + default: + /* Invalid S2K mode. */ + return CDK_Inv_Packet; + } + + s2k->hash_algo = cdk_stream_getc (inp); + if (s2k->mode == CDK_S2K_SALTED || s2k->mode == CDK_S2K_ITERSALTED) + { + if (stream_read (inp, s2k->salt, DIM (s2k->salt), &nread)) + return CDK_Inv_Packet; + if (nread != DIM (s2k->salt)) + return CDK_Inv_Packet; + + if (s2k->mode == CDK_S2K_ITERSALTED) + s2k->count = cdk_stream_getc (inp); + } + + ske->seskeylen = pktlen - 4 - minlen; + /* We check if there is an encrypted session key and if it fits into + the buffer. The maximal key length is 256-bit. */ + if (ske->seskeylen > DIM (ske->seskey)) + return CDK_Inv_Packet; + nleft = ske->seskeylen; + for (nread = 0; nread < ske->seskeylen; nread++) + { + ske->seskey[nread] = cdk_stream_getc (inp); + if (cdk_stream_eof (inp) && --nleft > 0) + return CDK_Inv_Packet; + } + + return 0; +} + + +static cdk_error_t +read_pubkey_enc (cdk_stream_t inp, size_t pktlen, cdk_pkt_pubkey_enc_t pke) +{ + size_t i, nenc; + + if (!inp || !pke) + return CDK_Inv_Value; + + if (DEBUG_PKT) + _cdk_log_debug ("read_pubkey_enc: %d octets\n", pktlen); + + if (pktlen < 12) + return CDK_Inv_Packet; + pke->version = cdk_stream_getc (inp); + if (pke->version < 2 || pke->version > 3) + return CDK_Inv_Packet; + pke->keyid[0] = read_32 (inp); + pke->keyid[1] = read_32 (inp); + if (!pke->keyid[0] && !pke->keyid[1]) + pke->throw_keyid = 1; /* RFC2440 "speculative" keyID */ + pke->pubkey_algo = cdk_stream_getc (inp); + nenc = cdk_pk_get_nenc (pke->pubkey_algo); + if (!nenc) + return CDK_Inv_Algo; + for (i = 0; i < nenc; i++) + { + cdk_error_t rc = read_mpi (inp, &pke->mpi[i], 0); + if (rc) + return rc; + } + + return 0; +} + + + +static cdk_error_t +read_mdc (cdk_stream_t inp, cdk_pkt_mdc_t mdc) +{ + size_t n; + cdk_error_t rc; + + if (!inp || !mdc) + return CDK_Inv_Value; + + if (DEBUG_PKT) + _cdk_log_debug ("read_mdc:\n"); + + rc = stream_read (inp, mdc->hash, DIM (mdc->hash), &n); + if (rc) + return rc; + + return n != DIM (mdc->hash) ? CDK_Inv_Packet : 0; +} + + +static cdk_error_t +read_compressed (cdk_stream_t inp, size_t pktlen, cdk_pkt_compressed_t c) +{ + if (!inp || !c) + return CDK_Inv_Value; + + if (DEBUG_PKT) + _cdk_log_debug ("read_compressed: %d octets\n", pktlen); + + c->algorithm = cdk_stream_getc (inp); + if (c->algorithm > 3) + return CDK_Inv_Packet; + + /* don't know the size, so we read until EOF */ + if (!pktlen) + { + c->len = 0; + c->buf = inp; + } + + /* FIXME: Support partial bodies. */ + return 0; +} + + +static cdk_error_t +read_public_key (cdk_stream_t inp, size_t pktlen, cdk_pkt_pubkey_t pk) +{ + size_t i, ndays, npkey; + + if (!inp || !pk) + return CDK_Inv_Value; + + if (DEBUG_PKT) + _cdk_log_debug ("read_public_key: %d octets\n", pktlen); + + pk->is_invalid = 1; /* default to detect missing self signatures */ + pk->is_revoked = 0; + pk->has_expired = 0; + + pk->version = cdk_stream_getc (inp); + if (pk->version < 2 || pk->version > 4) + return CDK_Inv_Packet_Ver; + pk->timestamp = read_32 (inp); + if (pk->version < 4) + { + ndays = read_16 (inp); + if (ndays) + pk->expiredate = pk->timestamp + ndays * 86400L; + } + + pk->pubkey_algo = cdk_stream_getc (inp); + npkey = cdk_pk_get_npkey (pk->pubkey_algo); + if (!npkey) + { + _cdk_log_debug ("invalid public key algorithm %d\n", pk->pubkey_algo); + return CDK_Inv_Algo; + } + for (i = 0; i < npkey; i++) + { + cdk_error_t rc = read_mpi (inp, &pk->mpi[i], 0); + if (rc) + return rc; + } + + /* These values are just for the first run and should be + replaced with the actual key flags from the self signature. */ + pk->pubkey_usage = _cdk_pk_algo_usage (pk->pubkey_algo); + return 0; +} + + +static cdk_error_t +read_public_subkey (cdk_stream_t inp, size_t pktlen, cdk_pkt_pubkey_t pk) +{ + if (!inp || !pk) + return CDK_Inv_Value; + return read_public_key (inp, pktlen, pk); +} + + +static cdk_error_t +read_secret_key (cdk_stream_t inp, size_t pktlen, cdk_pkt_seckey_t sk) +{ + size_t p1, p2, nread; + int i, nskey; + int rc; + + if (!inp || !sk || !sk->pk) + return CDK_Inv_Value; + + if (DEBUG_PKT) + _cdk_log_debug ("read_secret_key: %d octets\n", pktlen); + + p1 = cdk_stream_tell (inp); + rc = read_public_key (inp, pktlen, sk->pk); + if (rc) + return rc; + + sk->s2k_usage = cdk_stream_getc (inp); + sk->protect.sha1chk = 0; + if (sk->s2k_usage == 254 || sk->s2k_usage == 255) + { + sk->protect.sha1chk = (sk->s2k_usage == 254); + sk->protect.algo = cdk_stream_getc (inp); + sk->protect.s2k = cdk_calloc (1, sizeof *sk->protect.s2k); + if (!sk->protect.s2k) + return CDK_Out_Of_Core; + rc = read_s2k (inp, sk->protect.s2k); + if (rc) + return rc; + sk->protect.ivlen = gcry_cipher_get_algo_blklen (sk->protect.algo); + if (!sk->protect.ivlen) + return CDK_Inv_Packet; + rc = stream_read (inp, sk->protect.iv, sk->protect.ivlen, &nread); + if (rc) + return rc; + if (nread != sk->protect.ivlen) + return CDK_Inv_Packet; + } + else + sk->protect.algo = sk->s2k_usage; + if (sk->protect.algo == GCRY_CIPHER_NONE) + { + sk->csum = 0; + nskey = cdk_pk_get_nskey (sk->pk->pubkey_algo); + if (!nskey) + return CDK_Inv_Algo; + for (i = 0; i < nskey; i++) + { + rc = read_mpi (inp, &sk->mpi[i], 1); + if (rc) + return rc; + } + sk->csum = read_16 (inp); + sk->is_protected = 0; + } + else if (sk->pk->version < 4) + { + /* The length of each multiprecision integer is stored in plaintext. */ + nskey = cdk_pk_get_nskey (sk->pk->pubkey_algo); + if (!nskey) + return CDK_Inv_Algo; + for (i = 0; i < nskey; i++) + { + rc = read_mpi (inp, &sk->mpi[i], 1); + if (rc) + return rc; + } + sk->csum = read_16 (inp); + sk->is_protected = 1; + } + else + { + /* We need to read the rest of the packet because we do not + have any information how long the encrypted mpi's are */ + p2 = cdk_stream_tell (inp); + p2 -= p1; + sk->enclen = pktlen - p2; + if (sk->enclen < 2) + return CDK_Inv_Packet; /* at least 16 bits for the checksum! */ + sk->encdata = cdk_calloc (1, sk->enclen + 1); + if (!sk->encdata) + return CDK_Out_Of_Core; + if (stream_read (inp, sk->encdata, sk->enclen, &nread)) + return CDK_Inv_Packet; + nskey = cdk_pk_get_nskey (sk->pk->pubkey_algo); + if (!nskey) + return CDK_Inv_Algo; + /* We mark each MPI entry with NULL to indicate a protected key. */ + for (i = 0; i < nskey; i++) + sk->mpi[i] = NULL; + sk->is_protected = 1; + } + + sk->is_primary = 1; + _cdk_copy_pk_to_sk (sk->pk, sk); + return 0; +} + + +static cdk_error_t +read_secret_subkey (cdk_stream_t inp, size_t pktlen, cdk_pkt_seckey_t sk) +{ + cdk_error_t rc; + + if (!inp || !sk || !sk->pk) + return CDK_Inv_Value; + + rc = read_secret_key (inp, pktlen, sk); + sk->is_primary = 0; + return rc; +} + + +static cdk_error_t +read_attribute (cdk_stream_t inp, size_t pktlen, cdk_pkt_userid_t attr) +{ + const byte *p; + byte *buf; + size_t len, nread; + cdk_error_t rc; + + if (!inp || !attr || !pktlen) + return CDK_Inv_Value; + + if (DEBUG_PKT) + _cdk_log_debug ("read_attribute: %d octets\n", pktlen); + + strcpy (attr->name, "[attribute]"); + attr->len = strlen (attr->name); + buf = cdk_calloc (1, pktlen); + if (!buf) + return CDK_Out_Of_Core; + rc = stream_read (inp, buf, pktlen, &nread); + if (rc) + { + cdk_free (buf); + return CDK_Inv_Packet; + } + p = buf; + len = *p++; + pktlen--; + if (len == 255) + { + len = _cdk_buftou32 (p); + p += 4; + pktlen -= 4; + } + else if (len >= 192) + { + if (pktlen < 2) + { + cdk_free (buf); + return CDK_Inv_Packet; + } + len = ((len - 192) << 8) + *p + 192; + p++; + pktlen--; + } + + if (*p != 1) /* Currently only 1, meaning an image, is defined. */ + { + cdk_free (buf); + return CDK_Inv_Packet; + } + p++; + len--; + + if (pktlen - (len + 1) > 0) + return CDK_Inv_Packet; + attr->attrib_img = cdk_calloc (1, len); + if (!attr->attrib_img) + { + cdk_free (buf); + return CDK_Out_Of_Core; + } + attr->attrib_len = len; + memcpy (attr->attrib_img, p, len); + cdk_free (buf); + return rc; +} + + +static cdk_error_t +read_user_id (cdk_stream_t inp, size_t pktlen, cdk_pkt_userid_t user_id) +{ + size_t nread; + cdk_error_t rc; + + if (!inp || !user_id) + return CDK_Inv_Value; + if (!pktlen) + return CDK_Inv_Packet; + + if (DEBUG_PKT) + _cdk_log_debug ("read_user_id: %lu octets\n", pktlen); + + user_id->len = pktlen; + rc = stream_read (inp, user_id->name, pktlen, &nread); + if (rc) + return rc; + if (nread != pktlen) + return CDK_Inv_Packet; + user_id->name[nread] = '\0'; + return rc; +} + + +static cdk_error_t +read_subpkt (cdk_stream_t inp, cdk_subpkt_t * r_ctx, size_t * r_nbytes) +{ + byte c, c1; + size_t size, nread, n; + cdk_subpkt_t node; + cdk_error_t rc; + + if (!inp || !r_nbytes) + return CDK_Inv_Value; + + if (DEBUG_PKT) + _cdk_log_debug ("read_subpkt:\n"); + + n = 0; + *r_nbytes = 0; + c = cdk_stream_getc (inp); + n++; + if (c == 255) + { + size = read_32 (inp); + n += 4; + } + else if (c >= 192 && c < 255) + { + c1 = cdk_stream_getc (inp); + n++; + if (c1 == 0) + return 0; + size = ((c - 192) << 8) + c1 + 192; + } + else if (c < 192) + size = c; + else + return CDK_Inv_Packet; + + node = cdk_subpkt_new (size); + if (!node) + return CDK_Out_Of_Core; + node->size = size; + node->type = cdk_stream_getc (inp); + if (DEBUG_PKT) + _cdk_log_debug (" %d octets %d type\n", node->size, node->type); + n++; + node->size--; + rc = stream_read (inp, node->d, node->size, &nread); + n += nread; + if (rc) + return rc; + *r_nbytes = n; + if (!*r_ctx) + *r_ctx = node; + else + cdk_subpkt_add (*r_ctx, node); + return rc; +} + + +static cdk_error_t +read_onepass_sig (cdk_stream_t inp, size_t pktlen, cdk_pkt_onepass_sig_t sig) +{ + if (!inp || !sig) + return CDK_Inv_Value; + + if (DEBUG_PKT) + _cdk_log_debug ("read_onepass_sig: %d octets\n", pktlen); + + if (pktlen != 13) + return CDK_Inv_Packet; + sig->version = cdk_stream_getc (inp); + if (sig->version != 3) + return CDK_Inv_Packet_Ver; + sig->sig_class = cdk_stream_getc (inp); + sig->digest_algo = cdk_stream_getc (inp); + sig->pubkey_algo = cdk_stream_getc (inp); + sig->keyid[0] = read_32 (inp); + sig->keyid[1] = read_32 (inp); + sig->last = cdk_stream_getc (inp); + return 0; +} + + +static cdk_error_t +parse_sig_subpackets (cdk_pkt_signature_t sig) +{ + cdk_subpkt_t node; + + /* Setup the standard packet entries, so we can use V4 + signatures similar to V3. */ + for (node = sig->unhashed; node; node = node->next) + { + if (node->type == CDK_SIGSUBPKT_ISSUER && node->size >= 8) + { + sig->keyid[0] = _cdk_buftou32 (node->d); + sig->keyid[1] = _cdk_buftou32 (node->d + 4); + } + else if (node->type == CDK_SIGSUBPKT_EXPORTABLE && node->d[0] == 0) + { + /* Sometimes this packet might be placed in the unhashed area */ + sig->flags.exportable = 0; + } + } + for (node = sig->hashed; node; node = node->next) + { + if (node->type == CDK_SIGSUBPKT_SIG_CREATED && node->size >= 4) + sig->timestamp = _cdk_buftou32 (node->d); + else if (node->type == CDK_SIGSUBPKT_SIG_EXPIRE && node->size >= 4) + { + sig->expiredate = _cdk_buftou32 (node->d); + if (sig->expiredate > 0 && sig->expiredate < (u32) time (NULL)) + sig->flags.expired = 1; + } + else if (node->type == CDK_SIGSUBPKT_POLICY) + sig->flags.policy_url = 1; + else if (node->type == CDK_SIGSUBPKT_NOTATION) + sig->flags.notation = 1; + else if (node->type == CDK_SIGSUBPKT_REVOCABLE && node->d[0] == 0) + sig->flags.revocable = 0; + else if (node->type == CDK_SIGSUBPKT_EXPORTABLE && node->d[0] == 0) + sig->flags.exportable = 0; + } + if (sig->sig_class == 0x1F) + { + cdk_desig_revoker_t r, rnode; + + for (node = sig->hashed; node; node = node->next) + { + if (node->type == CDK_SIGSUBPKT_REV_KEY) + { + if (node->size < 22) + continue; + rnode = cdk_calloc (1, sizeof *rnode); + if (!rnode) + return CDK_Out_Of_Core; + rnode->r_class = node->d[0]; + rnode->algid = node->d[1]; + memcpy (rnode->fpr, node->d + 2, KEY_FPR_LEN); + if (!sig->revkeys) + sig->revkeys = rnode; + else + { + for (r = sig->revkeys; r->next; r = r->next) + ; + r->next = rnode; + } + } + } + } + + return 0; +} + + +static cdk_error_t +read_signature (cdk_stream_t inp, size_t pktlen, cdk_pkt_signature_t sig) +{ + size_t nbytes; + size_t i, size, nsig; + cdk_error_t rc; + + if (!inp || !sig) + return CDK_Inv_Value; + + if (DEBUG_PKT) + _cdk_log_debug ("read_signature: %d octets\n", pktlen); + + if (pktlen < 16) + return CDK_Inv_Packet; + sig->version = cdk_stream_getc (inp); + if (sig->version < 2 || sig->version > 4) + return CDK_Inv_Packet_Ver; + + sig->flags.exportable = 1; + sig->flags.revocable = 1; + + if (sig->version < 4) + { + if (cdk_stream_getc (inp) != 5) + return CDK_Inv_Packet; + sig->sig_class = cdk_stream_getc (inp); + sig->timestamp = read_32 (inp); + sig->keyid[0] = read_32 (inp); + sig->keyid[1] = read_32 (inp); + sig->pubkey_algo = cdk_stream_getc (inp); + sig->digest_algo = cdk_stream_getc (inp); + sig->digest_start[0] = cdk_stream_getc (inp); + sig->digest_start[1] = cdk_stream_getc (inp); + nsig = cdk_pk_get_nsig (sig->pubkey_algo); + if (!nsig) + return CDK_Inv_Algo; + for (i = 0; i < nsig; i++) + { + rc = read_mpi (inp, &sig->mpi[i], 0); + if (rc) + return rc; + } + } + else + { + sig->sig_class = cdk_stream_getc (inp); + sig->pubkey_algo = cdk_stream_getc (inp); + sig->digest_algo = cdk_stream_getc (inp); + sig->hashed_size = read_16 (inp); + size = sig->hashed_size; + sig->hashed = NULL; + while (size > 0) + { + rc = read_subpkt (inp, &sig->hashed, &nbytes); + if (rc) + return rc; + size -= nbytes; + } + sig->unhashed_size = read_16 (inp); + size = sig->unhashed_size; + sig->unhashed = NULL; + while (size > 0) + { + rc = read_subpkt (inp, &sig->unhashed, &nbytes); + if (rc) + return rc; + size -= nbytes; + } + + rc = parse_sig_subpackets (sig); + if (rc) + return rc; + + sig->digest_start[0] = cdk_stream_getc (inp); + sig->digest_start[1] = cdk_stream_getc (inp); + nsig = cdk_pk_get_nsig (sig->pubkey_algo); + if (!nsig) + return CDK_Inv_Algo; + for (i = 0; i < nsig; i++) + { + rc = read_mpi (inp, &sig->mpi[i], 0); + if (rc) + return rc; + } + } + + return 0; +} + + +static cdk_error_t +read_literal (cdk_stream_t inp, size_t pktlen, + cdk_pkt_literal_t * ret_pt, int is_partial) +{ + cdk_pkt_literal_t pt = *ret_pt; + size_t nread; + cdk_error_t rc; + + if (!inp || !pt) + return CDK_Inv_Value; + + if (DEBUG_PKT) + _cdk_log_debug ("read_literal: %d octets\n", pktlen); + + pt->mode = cdk_stream_getc (inp); + if (pt->mode != 0x62 && pt->mode != 0x74 && pt->mode != 0x75) + return CDK_Inv_Packet; + if (cdk_stream_eof (inp)) + return CDK_Inv_Packet; + + pt->namelen = cdk_stream_getc (inp); + if (pt->namelen > 0) + { + *ret_pt = pt = cdk_realloc (pt, sizeof *pt + pt->namelen + 1); + if (!pt) + return CDK_Out_Of_Core; + rc = stream_read (inp, pt->name, pt->namelen, &nread); + if (rc) + return rc; + if (nread != pt->namelen) + return CDK_Inv_Packet; + pt->name[pt->namelen] = '\0'; + } + pt->timestamp = read_32 (inp); + pktlen = pktlen - 6 - pt->namelen; + if (is_partial) + _cdk_stream_set_blockmode (inp, pktlen); + pt->buf = inp; + pt->len = pktlen; + return 0; +} + + +/* Read an old packet CTB and return the length of the body. */ +static void +read_old_length (cdk_stream_t inp, int ctb, size_t * r_len, size_t * r_size) +{ + int llen = ctb & 0x03; + + if (llen == 0) + { + *r_len = cdk_stream_getc (inp); + (*r_size)++; + } + else if (llen == 1) + { + *r_len = read_16 (inp); + (*r_size) += 2; + } + else if (llen == 2) + { + *r_len = read_32 (inp); + (*r_size) += 4; + } + else + { + *r_len = 0; + *r_size = 0; + } +} + + +/* Read a new CTB and decode the body length. */ +static void +read_new_length (cdk_stream_t inp, + size_t * r_len, size_t * r_size, size_t * r_partial) +{ + int c, c1; + + c = cdk_stream_getc (inp); + (*r_size)++; + if (c < 192) + *r_len = c; + else if (c >= 192 && c <= 223) + { + c1 = cdk_stream_getc (inp); + (*r_size)++; + *r_len = ((c - 192) << 8) + c1 + 192; + } + else if (c == 255) + { + *r_len = read_32 (inp); + (*r_size) += 4; + } + else + { + *r_len = 1 << (c & 0x1f); + *r_partial = 1; + } +} + + +/* Skip the current packet body. */ +static void +skip_packet (cdk_stream_t inp, size_t pktlen) +{ + byte buf[BUFSIZE]; + size_t nread, buflen = DIM (buf); + + while (pktlen > 0) + { + stream_read (inp, buf, pktlen > buflen ? buflen : pktlen, &nread); + pktlen -= nread; + } + + assert (pktlen == 0); +} + + +/** + * cdk_pkt_read: + * @inp: the input stream + * @pkt: allocated packet handle to store the packet + * + * Parse the next packet on the @inp stream and return its contents in @pkt. + **/ +cdk_error_t +cdk_pkt_read (cdk_stream_t inp, cdk_packet_t pkt) +{ + int use_mdc = 0; + int ctb, is_newctb; + int pkttype; + size_t pktlen = 0, pktsize = 0, is_partial = 0; + cdk_error_t rc; + + if (!inp || !pkt) + return CDK_Inv_Value; + + ctb = cdk_stream_getc (inp); + if (cdk_stream_eof (inp) || ctb == EOF) + return CDK_EOF; + else if (!ctb) + return CDK_Inv_Packet; + + pktsize++; + if (!(ctb & 0x80)) + { + _cdk_log_info ("cdk_pkt_read: no openpgp data found. " + "(ctb=%02X; fpos=%02X)\n", ctb, cdk_stream_tell (inp)); + return CDK_Inv_Packet; + } + + if (ctb & 0x40) /* RFC2440 packet format. */ + { + pkttype = ctb & 0x3f; + is_newctb = 1; + } + else /* the old RFC1991 packet format. */ + { + pkttype = ctb & 0x3f; + pkttype >>= 2; + is_newctb = 0; + } + + if (pkttype > 63) + { + _cdk_log_info ("cdk_pkt_read: unknown type %d\n", pkttype); + return CDK_Inv_Packet; + } + + if (is_newctb) + read_new_length (inp, &pktlen, &pktsize, &is_partial); + else + read_old_length (inp, ctb, &pktlen, &pktsize); + + pkt->pkttype = pkttype; + pkt->pktlen = pktlen; + pkt->pktsize = pktsize + pktlen; + pkt->old_ctb = is_newctb ? 0 : 1; + + rc = 0; + switch (pkt->pkttype) + { + case CDK_PKT_ATTRIBUTE: + pkt->pkt.user_id = cdk_calloc (1, sizeof *pkt->pkt.user_id + + pkt->pktlen + 16 + 1); + if (!pkt->pkt.user_id) + return CDK_Out_Of_Core; + rc = read_attribute (inp, pktlen, pkt->pkt.user_id); + pkt->pkttype = CDK_PKT_ATTRIBUTE; + break; + + case CDK_PKT_USER_ID: + pkt->pkt.user_id = cdk_calloc (1, sizeof *pkt->pkt.user_id + + pkt->pktlen); + if (!pkt->pkt.user_id) + return CDK_Out_Of_Core; + rc = read_user_id (inp, pktlen, pkt->pkt.user_id); + break; + + case CDK_PKT_PUBLIC_KEY: + pkt->pkt.public_key = cdk_calloc (1, sizeof *pkt->pkt.public_key); + if (!pkt->pkt.public_key) + return CDK_Out_Of_Core; + rc = read_public_key (inp, pktlen, pkt->pkt.public_key); + break; + + case CDK_PKT_PUBLIC_SUBKEY: + pkt->pkt.public_key = cdk_calloc (1, sizeof *pkt->pkt.public_key); + if (!pkt->pkt.public_key) + return CDK_Out_Of_Core; + rc = read_public_subkey (inp, pktlen, pkt->pkt.public_key); + break; + + case CDK_PKT_SECRET_KEY: + pkt->pkt.secret_key = cdk_calloc (1, sizeof *pkt->pkt.secret_key); + if (!pkt->pkt.secret_key) + return CDK_Out_Of_Core; + pkt->pkt.secret_key->pk = cdk_calloc (1, + sizeof *pkt->pkt.secret_key->pk); + if (!pkt->pkt.secret_key->pk) + return CDK_Out_Of_Core; + rc = read_secret_key (inp, pktlen, pkt->pkt.secret_key); + break; + + case CDK_PKT_SECRET_SUBKEY: + pkt->pkt.secret_key = cdk_calloc (1, sizeof *pkt->pkt.secret_key); + if (!pkt->pkt.secret_key) + return CDK_Out_Of_Core; + pkt->pkt.secret_key->pk = cdk_calloc (1, + sizeof *pkt->pkt.secret_key->pk); + if (!pkt->pkt.secret_key->pk) + return CDK_Out_Of_Core; + rc = read_secret_subkey (inp, pktlen, pkt->pkt.secret_key); + break; + + case CDK_PKT_LITERAL: + pkt->pkt.literal = cdk_calloc (1, sizeof *pkt->pkt.literal); + if (!pkt->pkt.literal) + return CDK_Out_Of_Core; + rc = read_literal (inp, pktlen, &pkt->pkt.literal, is_partial); + break; + + case CDK_PKT_ONEPASS_SIG: + pkt->pkt.onepass_sig = cdk_calloc (1, sizeof *pkt->pkt.onepass_sig); + if (!pkt->pkt.onepass_sig) + return CDK_Out_Of_Core; + rc = read_onepass_sig (inp, pktlen, pkt->pkt.onepass_sig); + break; + + case CDK_PKT_SIGNATURE: + pkt->pkt.signature = cdk_calloc (1, sizeof *pkt->pkt.signature); + if (!pkt->pkt.signature) + return CDK_Out_Of_Core; + rc = read_signature (inp, pktlen, pkt->pkt.signature); + break; + + case CDK_PKT_ENCRYPTED_MDC: + case CDK_PKT_ENCRYPTED: + pkt->pkt.encrypted = cdk_calloc (1, sizeof *pkt->pkt.encrypted); + if (!pkt->pkt.encrypted) + return CDK_Out_Of_Core; + use_mdc = (pkt->pkttype == CDK_PKT_ENCRYPTED_MDC) ? 1 : 0; + rc = read_encrypted (inp, pktlen, pkt->pkt.encrypted, + is_partial, use_mdc); + break; + + case CDK_PKT_SYMKEY_ENC: + pkt->pkt.symkey_enc = cdk_calloc (1, sizeof *pkt->pkt.symkey_enc); + if (!pkt->pkt.symkey_enc) + return CDK_Out_Of_Core; + rc = read_symkey_enc (inp, pktlen, pkt->pkt.symkey_enc); + break; + + case CDK_PKT_PUBKEY_ENC: + pkt->pkt.pubkey_enc = cdk_calloc (1, sizeof *pkt->pkt.pubkey_enc); + if (!pkt->pkt.pubkey_enc) + return CDK_Out_Of_Core; + rc = read_pubkey_enc (inp, pktlen, pkt->pkt.pubkey_enc); + break; + + case CDK_PKT_COMPRESSED: + pkt->pkt.compressed = cdk_calloc (1, sizeof *pkt->pkt.compressed); + if (!pkt->pkt.compressed) + return CDK_Out_Of_Core; + rc = read_compressed (inp, pktlen, pkt->pkt.compressed); + break; + + case CDK_PKT_MDC: + pkt->pkt.mdc = cdk_calloc (1, sizeof *pkt->pkt.mdc); + if (!pkt->pkt.mdc) + return CDK_Out_Of_Core; + rc = read_mdc (inp, pkt->pkt.mdc); + break; + + default: + /* Skip all packets we don't understand */ + skip_packet (inp, pktlen); + break; + } + + return rc; +} diff --git a/src/daemon/https/opencdk/seskey.c b/src/daemon/https/opencdk/seskey.c new file mode 100644 index 00000000..54c25af2 --- /dev/null +++ b/src/daemon/https/opencdk/seskey.c @@ -0,0 +1,717 @@ +/* seskey.c - Session key routines + * Copyright (C) 2002, 2003, 2007 Timo Schulz + * Copyright (C) 1998-2000, 2002 Free Software Foundation, Inc. + * + * This file is part of OpenCDK. + * + * OpenCDK 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. + * + * OpenCDK 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. + */ +#ifdef HAVE_CONFIG_H +# include +#endif +#include +#include +#include + +#include "opencdk.h" +#include "main.h" +#include "packet.h" + + +/* We encode the MD in this way: + * + * 0 1 PAD(n bytes) 0 ASN(asnlen bytes) MD(len bytes) + * + * PAD consists of FF bytes. + */ +static cdk_error_t +do_encode_md (byte ** r_frame, size_t * r_flen, const byte * md, int algo, + size_t len, unsigned nbits, const byte * asn, size_t asnlen) +{ + byte *frame = NULL; + size_t nframe = (nbits + 7) / 8; + size_t i, n = 0; + + if (!asn || !md || !r_frame || !r_flen) + return CDK_Inv_Value; + + if (len + asnlen + 4 > nframe) + return CDK_General_Error; + + frame = cdk_calloc (1, nframe); + if (!frame) + return CDK_Out_Of_Core; + frame[n++] = 0; + frame[n++] = 1; + i = nframe - len - asnlen - 3; + if (i < 0) + { + cdk_free (frame); + return CDK_Inv_Value; + } + memset (frame + n, 0xFF, i); + n += i; + frame[n++] = 0; + memcpy (frame + n, asn, asnlen); + n += asnlen; + memcpy (frame + n, md, len); + n += len; + if (n != nframe) + { + cdk_free (frame); + return CDK_Inv_Value; + } + *r_frame = frame; + *r_flen = n; + return 0; +} + + + +/* RFC2437 format: + * + * 0 2 RND(n bytes) 0 [A DEK(k bytes) CSUM(2 bytes)] + * + * RND - randomized bytes for padding. + * A - cipher algorithm. + * DEK - random session key. + * CKSUM - algebraic checksum of the DEK. + */ + +/** + * cdk_dek_encode_pkcs1 + * @dek: DEK object + * @nbits: size of the multi precision integer frame + * @r_enc: output of the encoded multiprecision integer + * + * Encode the given random session key in the DEK object + * into a multiprecision integer. + **/ +cdk_error_t +cdk_dek_encode_pkcs1 (cdk_dek_t dek, size_t nbits, gcry_mpi_t * r_enc) +{ + gcry_mpi_t a = NULL; + gcry_error_t err; + byte *p, *frame; + size_t n; + size_t nframe; + size_t i; + u16 chksum; + + if (!r_enc || !dek) + return CDK_Inv_Value; + + *r_enc = NULL; + chksum = 0; + for (i = 0; i < dek->keylen; i++) + chksum += dek->key[i]; + nframe = (nbits + 7) / 8; + frame = cdk_salloc (nframe + 1, 1); + if (!frame) + return CDK_Out_Of_Core; + n = 0; + frame[n++] = 0x00; + frame[n++] = 0x02; + i = nframe - 6 - dek->keylen; + p = gcry_random_bytes (i, GCRY_STRONG_RANDOM); + /* Replace zero bytes by new values */ + for (;;) + { + size_t j, k; + byte *pp; + + /* count the zero bytes */ + for (j = k = 0; j < i; j++) + { + if (!p[j]) + k++; + } + if (!k) + break; /* No zeroes remain. */ + k += k / 128; /* better get some more */ + pp = gcry_random_bytes (k, GCRY_STRONG_RANDOM); + for (j = 0; j < i && k; j++) + { + if (!p[j]) + p[j] = pp[--k]; + } + cdk_free (pp); + } + memcpy (frame + n, p, i); + cdk_free (p); + n += i; + frame[n++] = 0; + frame[n++] = dek->algo; + memcpy (frame + n, dek->key, dek->keylen); + n += dek->keylen; + frame[n++] = chksum >> 8; + frame[n++] = chksum; + err = gcry_mpi_scan (&a, GCRYMPI_FMT_USG, frame, nframe, &nframe); + cdk_free (frame); + if (err) + return map_gcry_error (err); + *r_enc = a; + return 0; +} + + +/** + * cdk_dek_decode_pkcs1: + * @ret_dek: the decoded DEK object + * @esk: the pkcs#1 encoded session key. + * + * Decode the given multi precision integer in pkcs#1 and + * store it into the DEK object. + **/ +cdk_error_t +cdk_dek_decode_pkcs1 (cdk_dek_t * ret_dek, gcry_mpi_t esk) +{ + cdk_dek_t dek; + byte frame[MAX_MPI_BYTES + 2 + 1]; + size_t nframe, n; + u16 csum, csum2; + gcry_error_t err; + + if (!ret_dek || !esk) + return CDK_Inv_Value; + + *ret_dek = NULL; /* reset */ + nframe = DIM (frame) - 1; + err = gcry_mpi_print (GCRYMPI_FMT_USG, frame, nframe, &nframe, esk); + if (err) + return map_gcry_error (err); + dek = cdk_salloc (sizeof *dek, 1); + if (!dek) + return CDK_Out_Of_Core; + + /* Now get the DEK (data encryption key) from the frame + * + * 0 2 RND(n bytes) 0 A DEK(k bytes) CSUM(2 bytes) + * + * (gcry_mpi_print already removed the leading zero). + * + * RND are non-zero randow bytes. + * A is the cipher algorithm + * DEK is the encryption key (session key) with length k + * CSUM + */ + n = 0; + if (frame[n] != 2) + { + cdk_free (dek); + return CDK_Inv_Mode; + } + for (n++; n < nframe && frame[n]; n++) + ; + n++; + dek->keylen = nframe - (n + 1) - 2; + dek->algo = frame[n++]; + if (dek->keylen != gcry_cipher_get_algo_keylen (dek->algo)) + { + _cdk_log_debug ("pkcs1 decode: invalid cipher keylen %d\n", + dek->keylen); + cdk_free (dek); + return CDK_Inv_Algo; + } + csum = frame[nframe - 2] << 8; + csum |= frame[nframe - 1]; + memcpy (dek->key, frame + n, dek->keylen); + csum2 = 0; + for (n = 0; n < dek->keylen; n++) + csum2 += dek->key[n]; + if (csum != csum2) + { + _cdk_log_debug ("pkcs decode: checksum does not match\n"); + cdk_free (dek); + return CDK_Chksum_Error; + } + *ret_dek = dek; + return 0; +} + + +/* Encode the given digest into a pkcs#1 compatible format. */ +cdk_error_t +_cdk_digest_encode_pkcs1 (byte ** r_md, size_t * r_mdlen, int pk_algo, + const byte * md, int digest_algo, unsigned nbits) +{ + gcry_error_t err; + size_t dlen; + + if (!md || !r_md || !r_mdlen) + return CDK_Inv_Value; + + dlen = gcry_md_get_algo_dlen (digest_algo); + if (!dlen) + return CDK_Inv_Algo; + if (is_DSA (pk_algo)) /* DSS does not use a special encoding. */ + { + *r_md = cdk_malloc (dlen + 1); + if (!*r_md) + return CDK_Out_Of_Core; + *r_mdlen = dlen; + memcpy (*r_md, md, dlen); + return 0; + } + else + { + byte *asn; + size_t asnlen; + cdk_error_t rc; + + err = gcry_md_get_asnoid (digest_algo, NULL, &asnlen); + if (err) + return map_gcry_error (err); + asn = cdk_malloc (asnlen + 1); + if (!asn) + return CDK_Out_Of_Core; + err = gcry_md_get_asnoid (digest_algo, asn, &asnlen); + if (err) + { + cdk_free (asn); + return map_gcry_error (err); + } + rc = do_encode_md (r_md, r_mdlen, md, digest_algo, dlen, + nbits, asn, asnlen); + cdk_free (asn); + return rc; + } + return 0; +} + + +/* FIXME: The prompt should be provided in a more generic way. + Like: (keyid, algorithm, [user-id]) */ +static char * +passphrase_prompt (cdk_pkt_seckey_t sk) +{ + u32 keyid = cdk_pk_get_keyid (sk->pk, NULL); + int bits = cdk_pk_get_nbits (sk->pk), pk_algo = sk->pubkey_algo; + const char *algo = "???", *fmt; + char *p; + + if (is_RSA (pk_algo)) + algo = "RSA"; + else if (is_ELG (pk_algo)) + algo = "ELG"; + else if (is_DSA (pk_algo)) + algo = "DSA"; + + fmt = "%d-bit %s key, ID %08lX\nEnter Passphrase: "; + p = cdk_calloc (1, 64 + strlen (fmt) + strlen (algo) + 1); + if (!p) + return NULL; + sprintf (p, fmt, bits, algo, keyid); + return p; +} + + +/* Try to unprotect the secret key, if needed, automatically. + The passphrase callback is used to get the passphrase directly + from the user. */ +cdk_error_t +_cdk_sk_unprotect_auto (cdk_ctx_t hd, cdk_pkt_seckey_t sk) +{ + char *pw, *p; + cdk_error_t rc; + + if (!sk->is_protected) + return 0; + + p = passphrase_prompt (sk); + pw = _cdk_passphrase_get (hd, p); + cdk_free (p); + if (!pw) + return CDK_No_Passphrase; + + rc = cdk_sk_unprotect (sk, pw); + + wipemem (pw, strlen (pw)); + cdk_free (pw); + return rc; +} + + +/** + * cdk_dek_extract: + * @ret_dek: the raw DEK object + * @hd: the session handle + * @enc: the public key encrypted packet + * @sk: the secret key. + * + * Try to extract the DEK from the public key encrypted packet. + **/ +cdk_error_t +cdk_dek_extract (cdk_dek_t * ret_dek, cdk_ctx_t hd, + cdk_pkt_pubkey_enc_t enc, cdk_pkt_seckey_t sk) +{ + gcry_mpi_t skey = NULL; + cdk_dek_t dek; + cdk_error_t rc; + + if (!enc || !sk || !ret_dek) + return CDK_Inv_Value; + + /* FIXME: it is not very elegant that we need the session handle here. */ + if (sk->is_protected) + { + rc = _cdk_sk_unprotect_auto (hd, sk); + if (rc) + return rc; + } + + rc = cdk_pk_decrypt (sk, enc, &skey); + if (rc) + return rc; + + rc = cdk_dek_decode_pkcs1 (&dek, skey); + gcry_mpi_release (skey); + if (rc) + { + cdk_dek_free (dek); + dek = NULL; + } + *ret_dek = dek; + return rc; +} + + +/** + * cdk_dek_new: + * @r_dek: the new DEK object + * + * Create a new DEK object. + **/ +cdk_error_t +cdk_dek_new (cdk_dek_t * r_dek) +{ + cdk_dek_t dek; + + if (!r_dek) + return CDK_Inv_Value; + *r_dek = NULL; + dek = cdk_salloc (sizeof *dek, 1); + if (!dek) + return CDK_Out_Of_Core; + *r_dek = dek; + return 0; +} + + +/** + * cdk_dek_set_cipher: + * @dek: the DEK object + * @algo: the cipher algorithm to use + * + * Set the cipher for the given DEK object. + **/ +cdk_error_t +cdk_dek_set_cipher (cdk_dek_t dek, int algo) +{ + if (!dek) + return CDK_Inv_Value; + + if (!algo) + algo = GCRY_CIPHER_AES128; + if (gcry_cipher_test_algo (algo)) + return CDK_Inv_Algo; + dek->algo = algo; + dek->keylen = gcry_cipher_get_algo_keylen (dek->algo); + return 0; +} + +cdk_error_t +cdk_dek_get_cipher (cdk_dek_t dek, int *r_algo) +{ + if (!dek || !r_algo) + return CDK_Inv_Value; + + + *r_algo = dek->algo; + return 0; +} + + +/** + * cdk_dek_set_key: + * @dek: the DEK object + * @key: the random session key + * @keylen: the length of the session key. + * + * Set the random session key for the given DEK object. + * If @key and @keylen is NULL (0) a random key will be generated. + * In any case, cdk_dek_set_cipher must be called first. + **/ +cdk_error_t +cdk_dek_set_key (cdk_dek_t dek, const byte * key, size_t keylen) +{ + gcry_cipher_hd_t hd; + size_t i; + + if (!dek) + return CDK_Inv_Value; + + /* The given key must be compatible with the symmetric + cipher algorithm set before. */ + if (keylen > 0 && keylen != dek->keylen) + return CDK_Inv_Mode; + + if (!key && !keylen) + { + gcry_error_t err; + + /* Used to generate a random session key. The extra code is used + to detect weak keys, if they are possible at all. */ + err = gcry_cipher_open (&hd, dek->algo, GCRY_CIPHER_MODE_CFB, + GCRY_CIPHER_ENABLE_SYNC); + if (err) + return map_gcry_error (err); + gcry_randomize (dek->key, dek->keylen, GCRY_STRONG_RANDOM); + for (i = 0; i < 8; i++) + { + if (!gcry_cipher_setkey (hd, dek->key, dek->keylen)) + { + gcry_cipher_close (hd); + return 0; + } + gcry_randomize (dek->key, dek->keylen, GCRY_STRONG_RANDOM); + } + gcry_cipher_close (hd); + return CDK_Weak_Key; + } + + memcpy (dek->key, key, dek->keylen); + return 0; +} + + +/** + * cdk_dek_set_mdc_flag: + * @dek: the DEK object + * @val: value to enable or disable the use + * + * Enable or disable the MDC flag for the given DEK object. + **/ +void +cdk_dek_set_mdc_flag (cdk_dek_t dek, int val) +{ + if (dek) + dek->use_mdc = val; +} + + +int +cdk_dek_get_mdc_flag (cdk_dek_t dek) +{ + if (!dek) + return 0; + return dek->use_mdc; +} + + +/** + * cdk_dek_free: + * @dek: the DEK object + * + * Release the DEK object. + **/ +void +cdk_dek_free (cdk_dek_t dek) +{ + if (!dek) + return; + + /* Make sure sentensive data is overwritten. */ + wipemem (dek->key, sizeof (dek->key)); + cdk_free (dek); +} + + +/* Hash the passphrase to produce the a DEK. + If create is set, a random salt will be generated. */ +static cdk_error_t +hash_passphrase (cdk_dek_t dek, const char *pw, cdk_s2k_t s2k, int create) +{ + gcry_md_hd_t md; + byte zero[1] = { 0x00 }; + int pass, i; + int used = 0, pwlen; + gcry_error_t err; + + if (!dek || !pw || !s2k) + return CDK_Inv_Value; + + if (!s2k->hash_algo) + s2k->hash_algo = GCRY_MD_SHA1; + pwlen = strlen (pw); + + dek->keylen = gcry_cipher_get_algo_keylen (dek->algo); + err = gcry_md_open (&md, s2k->hash_algo, 0); + if (err) + return map_gcry_error (err); + + for (pass = 0; used < dek->keylen; pass++) + { + if (pass) + { + gcry_md_reset (md); + for (i = 0; i < pass; i++) /* preset the hash context */ + gcry_md_write (md, zero, 1); + } + if (s2k->mode == CDK_S2K_SALTED || s2k->mode == CDK_S2K_ITERSALTED) + { + int len2 = pwlen + 8; + u32 count = len2; + if (create && !pass) + { + gcry_randomize (s2k->salt, 8, GCRY_STRONG_RANDOM); + if (s2k->mode == 3) + s2k->count = 96; /* 65536 iterations */ + } + if (s2k->mode == 3) + { + count = (16ul + (s2k->count & 15)) << ((s2k->count >> 4) + 6); + if (count < len2) + count = len2; + } + /* a little bit complicated because we need a ulong for count */ + while (count > len2) + { /* maybe iterated+salted */ + gcry_md_write (md, s2k->salt, 8); + gcry_md_write (md, pw, pwlen); + count -= len2; + } + if (count < 8) + gcry_md_write (md, s2k->salt, count); + else + { + gcry_md_write (md, s2k->salt, 8); + count -= 8; + gcry_md_write (md, pw, count); + } + } + else + gcry_md_write (md, pw, pwlen); + gcry_md_final (md); + i = gcry_md_get_algo_dlen (s2k->hash_algo); + if (i > dek->keylen - used) + i = dek->keylen - used; + memcpy (dek->key + used, gcry_md_read (md, s2k->hash_algo), i); + used += i; + } + gcry_md_close (md); + return 0; +} + + +/** + * cdk_dek_from_passphrase: + * @ret_dek: the new DEK. + * @cipher_algo: symmetric key algorithm to use + * @s2k: the S2K to use + * @rndsalt: 1=create random salt + * @pw: the passphrase. + * + * Transform a passphrase into a DEK object. + */ +cdk_error_t +cdk_dek_from_passphrase (cdk_dek_t * ret_dek, int cipher_algo, cdk_s2k_t s2k, + int rndsalt, const char *pw) +{ + cdk_dek_t dek; + cdk_error_t rc; + + if (!ret_dek) + return CDK_Inv_Value; + + *ret_dek = NULL; + rc = cdk_dek_new (&dek); + if (rc) + return rc; + rc = cdk_dek_set_cipher (dek, cipher_algo); + if (!rc) + rc = hash_passphrase (dek, pw, s2k, rndsalt); + if (rc) + { + cdk_dek_free (dek); + return rc; + } + + *ret_dek = dek; + return 0; +} + + +/** + * cdk_s2k_new: + * @ret_s2k: output for the new S2K object + * @mode: the S2K mode (simple, salted, iter+salted) + * @digest_algo: the hash algorithm + * @salt: random salt + * + * Create a new S2K object with the given parameter. + * The @salt parameter must be always 8 octets. + **/ +cdk_error_t +cdk_s2k_new (cdk_s2k_t * ret_s2k, int mode, int digest_algo, + const byte * salt) +{ + cdk_s2k_t s2k; + + if (!ret_s2k) + return CDK_Inv_Value; + + if (mode != 0x00 && mode != 0x01 && mode != 0x03) + return CDK_Inv_Mode; + + if (gcry_md_test_algo (digest_algo)) + return CDK_Inv_Algo; + + s2k = cdk_calloc (1, sizeof *s2k); + if (!s2k) + return CDK_Out_Of_Core; + s2k->mode = mode; + s2k->hash_algo = digest_algo; + if (salt) + memcpy (s2k->salt, salt, 8); + *ret_s2k = s2k; + return 0; +} + + +/** + * cdk_s2k_free: + * @s2k: the S2K object + * + * Release the given S2K object. + **/ +void +cdk_s2k_free (cdk_s2k_t s2k) +{ + cdk_free (s2k); +} + + +/* Make a copy of the source s2k into R_DST. */ +cdk_error_t +_cdk_s2k_copy (cdk_s2k_t * r_dst, cdk_s2k_t src) +{ + cdk_s2k_t dst; + cdk_error_t err; + + err = cdk_s2k_new (&dst, src->mode, src->hash_algo, src->salt); + if (err) + return err; + dst->count = src->count; + *r_dst = dst; + + return 0; +} diff --git a/src/daemon/https/opencdk/sig-check.c b/src/daemon/https/opencdk/sig-check.c new file mode 100644 index 00000000..f1ad5b2f --- /dev/null +++ b/src/daemon/https/opencdk/sig-check.c @@ -0,0 +1,489 @@ +/* sig-check.c - Check signatures + * Copyright (C) 2001, 2002, 2003, 2007 Timo Schulz + * Copyright (C) 1998-2002 Free Software Foundation, Inc. + * + * This file is part of OpenCDK. + * + * OpenCDK 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. + * + * OpenCDK 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. + */ +#ifdef HAVE_CONFIG_H +# include +#endif +#include +#include +#include +#include + +#include "opencdk.h" +#include "main.h" +#include "packet.h" + + +/* Hash all multi precision integers of the key PK with the given + message digest context MD. */ +static int +hash_mpibuf (cdk_pubkey_t pk, gcry_md_hd_t md, int usefpr) +{ + byte buf[MAX_MPI_BYTES]; /* FIXME: do not use hardcoded length. */ + size_t nbytes; + size_t i, npkey; + gcry_error_t err; + + /* We have to differ between two modes for v3 keys. To form the + fingerprint, we hash the MPI values without the length prefix. + But if we calculate the hash for verifying/signing we use all data. */ + npkey = cdk_pk_get_npkey (pk->pubkey_algo); + for (i = 0; i < npkey; i++) + { + err = gcry_mpi_print (GCRYMPI_FMT_PGP, buf, MAX_MPI_BYTES, + &nbytes, pk->mpi[i]); + if (err) + return map_gcry_error (err); + if (!usefpr || pk->version == 4) + gcry_md_write (md, buf, nbytes); + else /* without the prefix. */ + gcry_md_write (md, buf + 2, nbytes - 2); + } + return 0; +} + + +/* Hash an entire public key PK with the given message digest context + MD. The @usefpr param is only valid for version 3 keys because of + the different way to calculate the fingerprint. */ +cdk_error_t +_cdk_hash_pubkey (cdk_pubkey_t pk, gcry_md_hd_t md, int usefpr) +{ + byte buf[12]; + size_t i, n, npkey; + + if (!pk || !md) + return CDK_Inv_Value; + + if (usefpr && pk->version < 4 && is_RSA (pk->pubkey_algo)) + return hash_mpibuf (pk, md, 1); + + /* The version 4 public key packet does not have the 2 octets for + the expiration date. */ + n = pk->version < 4 ? 8 : 6; + npkey = cdk_pk_get_npkey (pk->pubkey_algo); + for (i = 0; i < npkey; i++) + n = n + (gcry_mpi_get_nbits (pk->mpi[i]) + 7) / 8 + 2; + + i = 0; + buf[i++] = 0x99; + buf[i++] = n >> 8; + buf[i++] = n >> 0; + buf[i++] = pk->version; + buf[i++] = pk->timestamp >> 24; + buf[i++] = pk->timestamp >> 16; + buf[i++] = pk->timestamp >> 8; + buf[i++] = pk->timestamp >> 0; + + if (pk->version < 4) + { + u16 a = 0; + + /* Convert the expiration date into days. */ + if (pk->expiredate) + a = (u16) ((pk->expiredate - pk->timestamp) / 86400L); + buf[i++] = a >> 8; + buf[i++] = a; + } + buf[i++] = pk->pubkey_algo; + gcry_md_write (md, buf, i); + return hash_mpibuf (pk, md, 0); +} + + +/* Hash the user ID @uid with the given message digest @md. + Use openpgp mode if @is_v4 is 1. */ +cdk_error_t +_cdk_hash_userid (cdk_pkt_userid_t uid, int is_v4, gcry_md_hd_t md) +{ + const byte *data; + byte buf[5]; + u32 dlen; + + if (!uid || !md) + return CDK_Inv_Value; + + if (!is_v4) + { + gcry_md_write (md, (byte *) uid->name, uid->len); + return 0; + } + + dlen = uid->attrib_img ? uid->attrib_len : uid->len; + data = uid->attrib_img ? uid->attrib_img : (byte *) uid->name; + buf[0] = uid->attrib_img ? 0xD1 : 0xB4; + buf[1] = dlen >> 24; + buf[2] = dlen >> 16; + buf[3] = dlen >> 8; + buf[4] = dlen >> 0; + gcry_md_write (md, buf, 5); + gcry_md_write (md, data, dlen); + return 0; +} + + +/* Hash all parts of the signature which are needed to derive + the correct message digest to verify the sig. */ +cdk_error_t +_cdk_hash_sig_data (cdk_pkt_signature_t sig, gcry_md_hd_t md) +{ + byte buf[4]; + + if (!sig || !md) + return CDK_Inv_Value; + + if (sig->version == 4) + gcry_md_putc (md, sig->version); + gcry_md_putc (md, sig->sig_class); + if (sig->version < 4) + { + buf[0] = sig->timestamp >> 24; + buf[1] = sig->timestamp >> 16; + buf[2] = sig->timestamp >> 8; + buf[3] = sig->timestamp >> 0; + gcry_md_write (md, buf, 4); + } + else + { + size_t n; + + gcry_md_putc (md, sig->pubkey_algo); + gcry_md_putc (md, sig->digest_algo); + if (sig->hashed != NULL) + { + byte *p = _cdk_subpkt_get_array (sig->hashed, 0, &n); + assert (p != NULL); + buf[0] = n >> 8; + buf[1] = n >> 0; + gcry_md_write (md, buf, 2); + gcry_md_write (md, p, n); + cdk_free (p); + sig->hashed_size = n; + n = sig->hashed_size + 6; + } + else + { + gcry_md_putc (md, 0x00); + gcry_md_putc (md, 0x00); + n = 6; + } + gcry_md_putc (md, sig->version); + gcry_md_putc (md, 0xFF); + buf[0] = n >> 24; + buf[1] = n >> 16; + buf[2] = n >> 8; + buf[3] = n >> 0; + gcry_md_write (md, buf, 4); + } + return 0; +} + + +/* Cache the signature result and store it inside the sig. */ +static void +cache_sig_result (cdk_pkt_signature_t sig, int res) +{ + sig->flags.checked = 0; + sig->flags.valid = 0; + if (res == 0) + { + sig->flags.checked = 1; + sig->flags.valid = 1; + } + else if (res == CDK_Bad_Sig) + { + sig->flags.checked = 1; + sig->flags.valid = 0; + } +} + + +/* Check the given signature @sig with the public key @pk. + Use the digest handle @digest. */ +cdk_error_t +_cdk_sig_check (cdk_pubkey_t pk, cdk_pkt_signature_t sig, + gcry_md_hd_t digest, int *r_expired) +{ + cdk_error_t rc; + byte md[MAX_DIGEST_LEN]; + time_t cur_time = (u32) time (NULL); + + if (!pk || !sig || !digest) + return CDK_Inv_Value; + + if (sig->flags.checked) + return sig->flags.valid ? 0 : CDK_Bad_Sig; + if (!KEY_CAN_SIGN (pk->pubkey_algo)) + return CDK_Inv_Algo; + if (pk->timestamp > sig->timestamp || pk->timestamp > cur_time) + return CDK_Time_Conflict; + + if (r_expired && pk->expiredate + && (pk->expiredate + pk->timestamp) > cur_time) + *r_expired = 1; + + _cdk_hash_sig_data (sig, digest); + gcry_md_final (digest); + memcpy (md, gcry_md_read (digest, sig->digest_algo), + gcry_md_get_algo_dlen (sig->digest_algo)); + + if (md[0] != sig->digest_start[0] || md[1] != sig->digest_start[1]) + return CDK_Chksum_Error; + + rc = cdk_pk_verify (pk, sig, md); + cache_sig_result (sig, rc); + return rc; +} + + +/* Check the given key signature. + @knode is the key node and @snode the signature node. */ +cdk_error_t +_cdk_pk_check_sig (cdk_keydb_hd_t keydb, + cdk_kbnode_t knode, cdk_kbnode_t snode, int *is_selfsig) +{ + gcry_md_hd_t md; + gcry_error_t err; + cdk_pubkey_t pk; + cdk_pkt_signature_t sig; + cdk_kbnode_t node; + cdk_error_t rc = 0; + int is_expired; + + if (!knode || !snode) + return CDK_Inv_Value; + + if (is_selfsig) + *is_selfsig = 0; + if (knode->pkt->pkttype != CDK_PKT_PUBLIC_KEY || + snode->pkt->pkttype != CDK_PKT_SIGNATURE) + return CDK_Inv_Value; + pk = knode->pkt->pkt.public_key; + sig = snode->pkt->pkt.signature; + + err = gcry_md_open (&md, sig->digest_algo, 0); + if (err) + return map_gcry_error (err); + + is_expired = 0; + if (sig->sig_class == 0x20) + { /* key revocation */ + cdk_kbnode_hash (knode, md, 0, 0, 0); + rc = _cdk_sig_check (pk, sig, md, &is_expired); + } + else if (sig->sig_class == 0x28) + { /* subkey revocation */ + node = cdk_kbnode_find_prev (knode, snode, CDK_PKT_PUBLIC_SUBKEY); + if (!node) + { /* no subkey for subkey revocation packet */ + rc = CDK_Error_No_Key; + goto fail; + } + cdk_kbnode_hash (knode, md, 0, 0, 0); + cdk_kbnode_hash (node, md, 0, 0, 0); + rc = _cdk_sig_check (pk, sig, md, &is_expired); + } + else if (sig->sig_class == 0x18 || sig->sig_class == 0x19) + { /* primary/secondary key binding */ + node = cdk_kbnode_find_prev (knode, snode, CDK_PKT_PUBLIC_SUBKEY); + if (!node) + { /* no subkey for subkey binding packet */ + rc = CDK_Error_No_Key; + goto fail; + } + cdk_kbnode_hash (knode, md, 0, 0, 0); + cdk_kbnode_hash (node, md, 0, 0, 0); + rc = _cdk_sig_check (pk, sig, md, &is_expired); + } + else if (sig->sig_class == 0x1F) + { /* direct key signature */ + cdk_kbnode_hash (knode, md, 0, 0, 0); + rc = _cdk_sig_check (pk, sig, md, &is_expired); + } + else + { /* all other classes */ + node = cdk_kbnode_find_prev (knode, snode, CDK_PKT_USER_ID); + if (!node) + { /* no user ID for key signature packet */ + rc = CDK_Error_No_Key; + goto fail; + } + cdk_kbnode_hash (knode, md, 0, 0, 0); + cdk_kbnode_hash (node, md, sig->version == 4, 0, 0); + if (pk->keyid[0] == sig->keyid[0] && pk->keyid[1] == sig->keyid[1]) + { + rc = _cdk_sig_check (pk, sig, md, &is_expired); + if (is_selfsig) + *is_selfsig = 1; + } + else if (keydb != NULL) + { + cdk_pubkey_t sig_pk; + + rc = cdk_keydb_get_pk (keydb, sig->keyid, &sig_pk); + if (!rc) + rc = _cdk_sig_check (sig_pk, sig, md, &is_expired); + cdk_pk_release (sig_pk); + } + } +fail: + gcry_md_close (md); + return rc; +} + + +/** + * cdk_pk_check_sigs: + * @key: the public key + * @hd: an optinal key database handle + * @r_status: variable to store the status of the key + * + * Check all signatures. When no key is available for checking, the + * sigstat is marked as 'NOKEY'. The @r_status contains the key flags + * which are or-ed or zero when there are no flags. + **/ +cdk_error_t +cdk_pk_check_sigs (cdk_kbnode_t key, cdk_keydb_hd_t keydb, int *r_status) +{ + cdk_pkt_signature_t sig; + cdk_kbnode_t node; + cdk_error_t rc; + u32 keyid; + int key_status, is_selfsig = 0; + int no_signer, n_sigs = 0; + + if (!key || !r_status) + return CDK_Inv_Value; + + *r_status = 0; + node = cdk_kbnode_find (key, CDK_PKT_PUBLIC_KEY); + if (!node) + return CDK_Error_No_Key; + + key_status = 0; + /* Continue with the signature check but adjust the + key status flags accordingly. */ + if (node->pkt->pkt.public_key->is_revoked) + key_status |= CDK_KEY_REVOKED; + if (node->pkt->pkt.public_key->has_expired) + key_status |= CDK_KEY_EXPIRED; + + rc = 0; + no_signer = 0; + keyid = cdk_pk_get_keyid (node->pkt->pkt.public_key, NULL); + for (node = key; node; node = node->next) + { + if (node->pkt->pkttype != CDK_PKT_SIGNATURE) + continue; + sig = node->pkt->pkt.signature; + rc = _cdk_pk_check_sig (keydb, key, node, &is_selfsig); + if (IS_UID_SIG (sig)) + { + if (is_selfsig == 0) + n_sigs++; + } + if (rc && IS_UID_SIG (sig) && rc == CDK_Error_No_Key) + { + /* We do not consider it a problem when the signing key + is not avaiable. We just mark the signature accordingly + and contine. */ + sig->flags.missing_key = 1; + no_signer++; + } + else if (rc && rc != CDK_Error_No_Key) + { + /* It might be possible that a single signature has been + corrupted, thus we do not consider it a problem when + one ore more signatures are bad. But at least the self + signature has to be valid. */ + if (is_selfsig) + { + key_status |= CDK_KEY_INVALID; + break; + } + } + _cdk_log_debug ("signature %s: signer %08lX keyid %08lX\n", + rc == CDK_Bad_Sig ? "BAD" : "good", sig->keyid[1], + keyid); + } + + if (n_sigs == no_signer) + key_status |= CDK_KEY_NOSIGNER; + *r_status = key_status; + if (rc == CDK_Error_No_Key) + rc = 0; + return rc; +} + + +/** + * cdk_pk_check_self_sig: + * @key: the key node + * @r_status: output the status of the key. + * + * A convenient function to make sure the key is valid. + * Valid means the self signature is ok. + **/ +cdk_error_t +cdk_pk_check_self_sig (cdk_kbnode_t key, int *r_status) +{ + cdk_pkt_signature_t sig; + cdk_kbnode_t node; + cdk_error_t rc; + u32 keyid[2], sigid[2]; + int is_selfsig, sig_ok; + + if (!key || !r_status) + return CDK_Inv_Value; + + node = cdk_kbnode_find (key, CDK_PKT_PUBLIC_KEY); + if (!node) + return CDK_Error_No_Key; + /* FIXME: we should set expire/revoke here also but callers + expect CDK_KEY_VALID=0 if the key is okay. */ + cdk_pk_get_keyid (key->pkt->pkt.public_key, keyid); + sig_ok = 0; + for (node = key; node; node = node->next) + { + if (node->pkt->pkttype != CDK_PKT_SIGNATURE) + continue; + sig = node->pkt->pkt.signature; + if (!IS_UID_SIG (sig)) + continue; + cdk_sig_get_keyid (sig, sigid); + if (sigid[0] != keyid[0] || sigid[1] != keyid[1]) + continue; + /* FIXME: Now we check all self signatures. */ + rc = _cdk_pk_check_sig (NULL, key, node, &is_selfsig); + if (rc) + { + *r_status = CDK_KEY_INVALID; + return rc; + } + else /* For each valid self sig we increase this counter. */ + sig_ok++; + } + + /* A key without a self signature is not valid. */ + if (!sig_ok) + { + *r_status = CDK_KEY_INVALID; + return CDK_General_Error; + } + /* No flags indicate a valid key. */ + *r_status = CDK_KEY_VALID; + return 0; +} diff --git a/src/daemon/https/opencdk/stream.c b/src/daemon/https/opencdk/stream.c new file mode 100644 index 00000000..8389401f --- /dev/null +++ b/src/daemon/https/opencdk/stream.c @@ -0,0 +1,1446 @@ +/* stream.c - The stream implementation + * Copyright (C) 2002, 2003, 2007 Timo Schulz + * + * This file is part of OpenCDK. + * + * OpenCDK 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. + * + * OpenCDK 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. + */ +#ifdef HAVE_CONFIG_H +#include +#endif +#include +#include +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +# include +#endif + +#include "opencdk.h" +#include "main.h" +#include "filters.h" +#include "stream.h" +#include "types.h" + +/* This is the maximal amount of bytes we map. */ +#define MAX_MAP_SIZE 16777216 + +static int stream_flush (cdk_stream_t s); +static int stream_filter_write (cdk_stream_t s); +static int stream_cache_flush (cdk_stream_t s, FILE * fp); + +/* Customized tmpfile() version from misc.c */ +FILE *my_tmpfile (void); + + +/* FIXME: The read/write/putc/getc function cannot directly + return an error code. It is stored in an error variable + inside the string. Right now there is no code to + return the error code or to reset it. */ + +/** + * cdk_stream_open: + * @file: The file to open + * @ret_s: The new STREAM object + * + * Create a new stream based on an existing file. The stream is + * opened in read-only mode. + **/ +cdk_error_t +cdk_stream_open (const char *file, cdk_stream_t * ret_s) +{ + return _cdk_stream_open_mode (file, "rb", ret_s); +} + + +/* Helper function to allow to open a stream in different modes. */ +cdk_error_t +_cdk_stream_open_mode (const char *file, const char *mode, + cdk_stream_t * ret_s) +{ + cdk_stream_t s; + + if (!file || !ret_s) + return CDK_Inv_Value; + + _cdk_log_debug ("open stream `%s'\n", file); + *ret_s = NULL; + s = cdk_calloc (1, sizeof *s); + if (!s) + return CDK_Out_Of_Core; + s->fname = cdk_strdup (file); + if (!s->fname) + { + cdk_free (s); + return CDK_Out_Of_Core; + } + s->fp = fopen (file, mode); + if (!s->fp) + { + cdk_free (s->fname); + cdk_free (s); + return CDK_File_Error; + } + _cdk_log_debug ("open stream fd=%d\n", fileno (s->fp)); + s->flags.write = 0; + *ret_s = s; + return 0; +} + + +/** + * cdk_stream_new_from_cbs: + * @cbs: the callback context with all user callback functions + * @opa: opaque handle which is passed to all callbacks. + * @ret_s: the allocated stream + * + * This function creates a stream which uses user callback + * for the core operations (open, close, read, write, seek). + */ +cdk_error_t +cdk_stream_new_from_cbs (cdk_stream_cbs_t cbs, void *opa, + cdk_stream_t * ret_s) +{ + cdk_stream_t s; + + if (!cbs || !opa || !ret_s) + return CDK_Inv_Value; + + *ret_s = NULL; + s = cdk_calloc (1, sizeof *s); + if (!s) + return CDK_Out_Of_Core; + + s->cbs.read = cbs->read; + s->cbs.write = cbs->write; + s->cbs.seek = cbs->seek; + s->cbs.release = cbs->release; + s->cbs.open = cbs->open; + s->cbs_hd = opa; + *ret_s = s; + + /* If there is a user callback for open, we need to call it + here because read/write expects an open stream. */ + if (s->cbs.open) + return s->cbs.open (s->cbs_hd); + return 0; +} + + +/** + * cdk_stream_new: Create a new stream into into the given file. + * @file: The name of the new file + * @ret_s: The new STREAM object + **/ +cdk_error_t +cdk_stream_new (const char *file, cdk_stream_t * ret_s) +{ + cdk_stream_t s; + + if (!ret_s) + return CDK_Inv_Value; + + _cdk_log_debug ("new stream `%s'\n", file ? file : "[temp]"); + *ret_s = NULL; + s = cdk_calloc (1, sizeof *s); + if (!s) + return CDK_Out_Of_Core; + s->flags.write = 1; + if (!file) + s->flags.temp = 1; + else + { + s->fname = cdk_strdup (file); + if (!s->fname) + { + cdk_free (s); + return CDK_Out_Of_Core; + } + } + s->fp = my_tmpfile (); + if (!s->fp) + { + cdk_free (s->fname); + cdk_free (s); + return CDK_File_Error; + } + _cdk_log_debug ("new stream fd=%d\n", fileno (s->fp)); + *ret_s = s; + return 0; +} + + +/** + * cdk_stream_create: create a new stream. + * @file: the filename + * @ret_s: the object + * + * The difference to cdk_stream_new is, that no filtering can be used with + * this kind of stream and everything is written directly to the stream. + **/ +cdk_error_t +cdk_stream_create (const char *file, cdk_stream_t * ret_s) +{ + cdk_stream_t s; + + if (!file || !ret_s) + return CDK_Inv_Value; + + _cdk_log_debug ("create stream `%s'\n", file); + *ret_s = NULL; + s = cdk_calloc (1, sizeof *s); + if (!s) + return CDK_Out_Of_Core; + s->flags.write = 1; + s->flags.filtrated = 1; + s->fname = cdk_strdup (file); + if (!s->fname) + { + cdk_free (s); + return CDK_Out_Of_Core; + } + s->fp = fopen (file, "w+b"); + if (!s->fp) + { + cdk_free (s->fname); + cdk_free (s); + return CDK_File_Error; + } + _cdk_log_debug ("stream create fd=%d\n", fileno (s->fp)); + *ret_s = s; + return 0; +} + + +/** + * cdk_stream_tmp_new: + * @r_out: the new temp stream. + * + * Allocate a new tempory stream which is not associated with a file. + */ +cdk_error_t +cdk_stream_tmp_new (cdk_stream_t * r_out) +{ + return cdk_stream_new (NULL, r_out); +} + + + +/** + * cdk_stream_tmp_from_mem: + * @buf: the buffer which shall be written to the temp stream. + * @buflen: how large the buffer is + * @r_out: the new stream with the given contents. + * + * Create a new tempory stream with the given contests. + */ +cdk_error_t +cdk_stream_tmp_from_mem (const void *buf, size_t buflen, cdk_stream_t * r_out) +{ + cdk_stream_t s; + cdk_error_t rc; + int nwritten; + + *r_out = NULL; + rc = cdk_stream_tmp_new (&s); + if (rc) + return rc; + + nwritten = cdk_stream_write (s, buf, buflen); + if (nwritten == EOF) + { + cdk_stream_close (s); + return s->error; + } + cdk_stream_seek (s, 0); + *r_out = s; + return 0; +} + + +cdk_error_t +_cdk_stream_fpopen (FILE * fp, unsigned write_mode, cdk_stream_t * ret_out) +{ + cdk_stream_t s; + + *ret_out = NULL; + s = cdk_calloc (1, sizeof *s); + if (!s) + return CDK_Out_Of_Core; + + _cdk_log_debug ("stream ref fd=%d\n", fileno (fp)); + s->fp = fp; + s->fp_ref = 1; + s->flags.filtrated = 1; + s->flags.write = write_mode; + + *ret_out = s; + return 0; +} + + +cdk_error_t +_cdk_stream_append (const char *file, cdk_stream_t * ret_s) +{ + cdk_stream_t s; + cdk_error_t rc; + + if (!ret_s) + return CDK_Inv_Value; + *ret_s = NULL; + + rc = _cdk_stream_open_mode (file, "a+b", &s); + if (rc) + return rc; + + /* In the append mode, we need to write to the flag. */ + s->flags.write = 1; + *ret_s = s; + return 0; +} + + +/** + * cdk_stream_is_compressed: + * @s: the stream + * + * Return 0 if the stream is uncompressed, otherwise the + * compression algorithm. + */ +int +cdk_stream_is_compressed (cdk_stream_t s) +{ + if (!s) + return 0; + return s->flags.compressed; +} + +void +_cdk_stream_set_compress_algo (cdk_stream_t s, int algo) +{ + if (!s) + return; + s->flags.compressed = algo; +} + + +cdk_error_t +cdk_stream_flush (cdk_stream_t s) +{ + cdk_error_t rc; + + if (!s) + return CDK_Inv_Value; + + /* The user callback does not support flush */ + if (s->cbs_hd) + return 0; + + /* For read-only streams, no flush is needed. */ + if (!s->flags.write) + return 0; + + if (!s->flags.filtrated) + { + if (!cdk_stream_get_length (s)) + return 0; + rc = cdk_stream_seek (s, 0); + if (!rc) + rc = stream_flush (s); + if (!rc) + rc = stream_filter_write (s); + s->flags.filtrated = 1; + if (rc) + { + s->error = rc; + return rc; + } + } + return 0; +} + + +void +cdk_stream_tmp_set_mode (cdk_stream_t s, int val) +{ + if (s && s->flags.temp) + s->fmode = val; +} + + +/** + * cdk_stream_close: Close a stream and flush all buffers. + * @s: The STREAM object. + * + * This function work different for read or write streams. When the + * stream is for reading, the filtering is already done and we can + * simply close the file and all buffers. + * But for the case it's a write stream, we need to apply all registered + * filters now. The file is closed in the filter function and not here. + **/ +cdk_error_t +cdk_stream_close (cdk_stream_t s) +{ + struct stream_filter_s *f, *f2; + cdk_error_t rc; + + if (!s) + return CDK_Inv_Value; + + _cdk_log_debug ("close stream ref=%d `%s'\n", + s->fp_ref, s->fname ? s->fname : "[temp]"); + + /* In the user callback mode, we call the release cb if possible + and just free the stream. */ + if (s->cbs_hd) + { + if (s->cbs.release) + rc = s->cbs.release (s->cbs_hd); + else + rc = 0; + cdk_free (s); + return rc; + } + + + rc = 0; + if (!s->flags.filtrated && !s->error) + rc = cdk_stream_flush (s); + if (!s->fp_ref && (s->fname || s->flags.temp)) + { + int err; + + _cdk_log_debug ("close stream fd=%d\n", fileno (s->fp)); + err = fclose (s->fp); + s->fp = NULL; + if (err) + rc = CDK_File_Error; + } + + /* Iterate over the filter list and use the cleanup flag to + free the allocated internal structures. */ + f = s->filters; + while (f) + { + f2 = f->next; + if (f->fnct) + f->fnct (f->opaque, STREAMCTL_FREE, NULL, NULL); + cdk_free (f); + f = f2; + } + + if (s->fname) + { + cdk_free (s->fname); + s->fname = NULL; + } + + cdk_free (s->cache.buf); + s->cache.alloced = 0; + + cdk_free (s); + return rc; +} + + +/** + * cdk_stream_eof: Return if the associated file handle was set to EOF. + * @s: The STREAM object. + * + * This function will only work with read streams. + **/ +int +cdk_stream_eof (cdk_stream_t s) +{ + return s ? s->flags.eof : -1; +} + + +const char * +_cdk_stream_get_fname (cdk_stream_t s) +{ + if (!s) + return NULL; + if (s->flags.temp) + return NULL; + return s->fname ? s->fname : NULL; +} + + +/* Return the underlying FP of the stream. + WARNING: This handle should not be closed. */ +FILE * +_cdk_stream_get_fp (cdk_stream_t s) +{ + return s ? s->fp : NULL; +} + + +int +_cdk_stream_get_errno (cdk_stream_t s) +{ + return s ? s->error : CDK_Inv_Value; +} + + +/** + * cdk_stream_get_length: Return the length of the associated file handle. + * @s: The STREAM object. + * + * This function should work for both read and write streams. For write + * streams an additional flush is used to write possible pending data. + **/ +off_t +cdk_stream_get_length (cdk_stream_t s) +{ + struct stat statbuf; + cdk_error_t rc; + + if (!s) + return (off_t) - 1; + + /* The user callback does not support stat. */ + if (s->cbs_hd) + return 0; + + rc = stream_flush (s); + if (rc) + { + s->error = rc; + return (off_t) - 1; + } + + if (fstat (fileno (s->fp), &statbuf)) + { + s->error = CDK_File_Error; + return (off_t) - 1; + } + + return statbuf.st_size; +} + + +static struct stream_filter_s * +filter_add2 (cdk_stream_t s) +{ + struct stream_filter_s *f; + + assert (s); + + f = cdk_calloc (1, sizeof *f); + if (!f) + return NULL; + f->next = s->filters; + s->filters = f; + return f; +} + + +static struct stream_filter_s * +filter_search (cdk_stream_t s, filter_fnct_t fnc) +{ + struct stream_filter_s *f; + + assert (s); + + for (f = s->filters; f; f = f->next) + { + if (f->fnct == fnc) + return f; + } + + return NULL; +} + + +struct stream_filter_s * +filter_add (cdk_stream_t s, filter_fnct_t fnc, int type) +{ + struct stream_filter_s *f; + + assert (s); + + s->flags.filtrated = 0; + f = filter_search (s, fnc); + if (f) + return f; + f = filter_add2 (s); + if (!f) + return NULL; + f->fnct = fnc; + f->flags.enabled = 1; + f->tmp = NULL; + f->type = type; + switch (type) + { + case fARMOR: + f->opaque = &f->u.afx; + break; + case fCIPHER: + f->opaque = &f->u.cfx; + break; + case fLITERAL: + f->opaque = &f->u.pfx; + break; + case fCOMPRESS: + f->opaque = &f->u.zfx; + break; + case fHASH: + f->opaque = &f->u.mfx; + break; + case fTEXT: + f->opaque = &f->u.tfx; + break; + default: + f->opaque = NULL; + } + + return f; +} + + +static int +stream_get_mode (cdk_stream_t s) +{ + assert (s); + + if (s->flags.temp) + return s->fmode; + return s->flags.write; +} + + +static filter_fnct_t +stream_id_to_filter (int type) +{ + switch (type) + { + case fARMOR: + return _cdk_filter_armor; + case fLITERAL: + return _cdk_filter_literal; + case fTEXT: + return _cdk_filter_text; + case fCIPHER: + return _cdk_filter_cipher; + case fCOMPRESS: + return _cdk_filter_compress; + default: + return NULL; + } +} + + +/** + * cdk_stream_filter_disable: Disable the filter with the type 'type' + * @s: The STREAM object + * @type: The numberic filter ID. + * + **/ +cdk_error_t +cdk_stream_filter_disable (cdk_stream_t s, int type) +{ + struct stream_filter_s *f; + filter_fnct_t fnc; + + if (!s) + return CDK_Inv_Value; + + fnc = stream_id_to_filter (type); + if (!fnc) + return CDK_Inv_Value; + f = filter_search (s, fnc); + if (f) + f->flags.enabled = 0; + return 0; +} + + +/* WARNING: tmp should not be closed by the caller. */ +static cdk_error_t +stream_fp_replace (cdk_stream_t s, FILE ** tmp) +{ + int rc; + + assert (s); + + _cdk_log_debug ("replace stream fd=%d with fd=%d\n", + fileno (s->fp), fileno (*tmp)); + rc = fclose (s->fp); + if (rc) + return CDK_File_Error; + s->fp = *tmp; + *tmp = NULL; + return 0; +} + + +/* This function is exactly like filter_read, except the fact that we can't + use tmpfile () all the time. That's why we open the real file when there + is no last filter. */ +static cdk_error_t +stream_filter_write (cdk_stream_t s) +{ + struct stream_filter_s *f; + cdk_error_t rc = 0; + + assert (s); + + if (s->flags.filtrated) + return CDK_Inv_Value; + + for (f = s->filters; f; f = f->next) + { + if (!f->flags.enabled) + continue; + /* if there is no next filter, create the final output file */ + _cdk_log_debug ("filter [write]: last filter=%d fname=%s\n", + f->next ? 1 : 0, s->fname); + if (!f->next && s->fname) + f->tmp = fopen (s->fname, "w+b"); + else + f->tmp = my_tmpfile (); + if (!f->tmp) + { + rc = CDK_File_Error; + break; + } + /* If there is no next filter, flush the cache. We also do this + when the next filter is the armor filter because this filter + is special and before it starts, all data should be written. */ + if ((!f->next || f->next->type == fARMOR) && s->cache.size) + { + rc = stream_cache_flush (s, f->tmp); + if (rc) + break; + } + rc = f->fnct (f->opaque, f->ctl, s->fp, f->tmp); + _cdk_log_debug ("filter [write]: type=%d rc=%d\n", f->type, rc); + if (!rc) + rc = stream_fp_replace (s, &f->tmp); + if (!rc) + rc = cdk_stream_seek (s, 0); + if (rc) + { + _cdk_log_debug ("filter [close]: fd=%d\n", fileno (f->tmp)); + fclose (f->tmp); + break; + } + } + return rc; +} + + +/* Here all data from the file handle is passed through all filters. + The scheme works like this: + Create a tempfile and use it for the output of the filter. Then the + original file handle will be closed and replace with the temp handle. + The file pointer will be set to the begin and the game starts again. */ +static cdk_error_t +stream_filter_read (cdk_stream_t s) +{ + struct stream_filter_s *f; + cdk_error_t rc = 0; + + assert (s); + + if (s->flags.filtrated) + return 0; + + for (f = s->filters; f; f = f->next) + { + if (!f->flags.enabled) + continue; + if (f->flags.error) + { + _cdk_log_debug ("filter %s [read]: has the error flag; skipped\n", + s->fname ? s->fname : "[temp]"); + continue; + } + + f->tmp = my_tmpfile (); + if (!f->tmp) + { + rc = CDK_File_Error; + break; + } + rc = f->fnct (f->opaque, f->ctl, s->fp, f->tmp); + _cdk_log_debug ("filter %s [read]: type=%d rc=%d\n", + s->fname ? s->fname : "[temp]", f->type, rc); + if (rc) + { + f->flags.error = 1; + break; + } + + f->flags.error = 0; + /* If the filter is read-only, do not replace the FP because + the contents were not altered in any way. */ + if (!f->flags.rdonly) + { + rc = stream_fp_replace (s, &f->tmp); + if (rc) + break; + } + else + { + fclose (f->tmp); + f->tmp = NULL; + } + rc = cdk_stream_seek (s, 0); + if (rc) + break; + /* Disable the filter after it was successfully used. The idea + is the following: let's say the armor filter was pushed and + later more filters were added. The second time the filter code + will be executed, only the new filter should be started but + not the old because we already used it. */ + f->flags.enabled = 0; + } + + return rc; +} + + +void * +_cdk_stream_get_opaque (cdk_stream_t s, int fid) +{ + struct stream_filter_s *f; + + if (!s) + return NULL; + + for (f = s->filters; f; f = f->next) + { + if (f->type == fid) + return f->opaque; + } + return NULL; +} + + +/** + * cdk_stream_read: Try to read count bytes from the STREAM object. + * @s: The STREAM object. + * @buf: The buffer to insert the readed bytes. + * @count: Request so much bytes. + * + * When this function is called the first time, it can take a while + * because all filters need to be processed. Please remember that you + * need to add the filters in reserved order. + **/ +int +cdk_stream_read (cdk_stream_t s, void *buf, size_t buflen) +{ + int nread; + int rc; + + if (!s) + { + s->error = CDK_Inv_Value; + return EOF; + } + + if (s->cbs_hd) + { + if (s->cbs.read) + return s->cbs.read (s->cbs_hd, buf, buflen); + return 0; + } + + if (s->flags.write && !s->flags.temp) + { + s->error = CDK_Inv_Mode; + return EOF; /* This is a write stream */ + } + + if (!s->flags.no_filter && !s->cache.on && !s->flags.filtrated) + { + rc = stream_filter_read (s); + if (rc) + { + s->error = rc; + if (feof (s->fp)) + s->flags.eof = 1; + return EOF; + } + s->flags.filtrated = 1; + } + if (!buf && !buflen) + return 0; + nread = fread (buf, 1, buflen, s->fp); + if (!nread) + nread = EOF; + if (feof (s->fp)) + { + s->error = 0; + s->flags.eof = 1; + } + return nread; +} + + +int +cdk_stream_getc (cdk_stream_t s) +{ + unsigned char buf[2]; + int nread; + + if (!s) + { + s->error = CDK_Inv_Value; + return EOF; + } + nread = cdk_stream_read (s, buf, 1); + if (nread == EOF) + { + s->error = CDK_File_Error; + return EOF; + } + return buf[0]; +} + + +/** + * cdk_stream_write: Try to write count bytes into the stream. + * @s: The STREAM object + * @buf: The buffer with the values to write. + * @count: The size of the buffer. + * + * In this function we simply write the bytes to the stream. We can't + * use the filters here because it would mean they have to support + * partial flushing. + **/ +int +cdk_stream_write (cdk_stream_t s, const void *buf, size_t count) +{ + int nwritten; + + if (!s) + { + s->error = CDK_Inv_Value; + return EOF; + } + + if (s->cbs_hd) + { + if (s->cbs.write) + return s->cbs.write (s->cbs_hd, buf, count); + return 0; + } + + if (!s->flags.write) + { + s->error = CDK_Inv_Mode; /* this is a read stream */ + return EOF; + } + + if (!buf && !count) + return stream_flush (s); + + if (s->cache.on) + { + /* We need to resize the buffer if the additional data wouldn't + fit into it. We allocate more memory to avoid to resize it the + next time the function is used. */ + if (s->cache.size + count > s->cache.alloced) + { + byte *old = s->cache.buf; + + s->cache.buf = + cdk_calloc (1, s->cache.alloced + count + STREAM_BUFSIZE); + s->cache.alloced += (count + STREAM_BUFSIZE); + memcpy (s->cache.buf, old, s->cache.size); + cdk_free (old); + _cdk_log_debug ("stream: enlarge cache to %d octets\n", + s->cache.alloced); + } + memcpy (s->cache.buf + s->cache.size, buf, count); + s->cache.size += count; + return count; + } + + nwritten = fwrite (buf, 1, count, s->fp); + if (!nwritten) + nwritten = EOF; + return nwritten; +} + + +int +cdk_stream_putc (cdk_stream_t s, int c) +{ + byte buf[2]; + int nwritten; + + if (!s) + { + s->error = CDK_Inv_Value; + return EOF; + } + buf[0] = c; + nwritten = cdk_stream_write (s, buf, 1); + if (nwritten == EOF) + return EOF; + return 0; +} + + +off_t +cdk_stream_tell (cdk_stream_t s) +{ + return s ? ftell (s->fp) : (off_t) - 1; +} + + +cdk_error_t +cdk_stream_seek (cdk_stream_t s, off_t offset) +{ + off_t len; + + if (!s) + return CDK_Inv_Value; + + if (s->cbs_hd) + { + if (s->cbs.seek) + return s->cbs.seek (s->cbs_hd, offset); + return 0; + } + + /* Set or reset the EOF flag. */ + len = cdk_stream_get_length (s); + if (len == offset) + s->flags.eof = 1; + else + s->flags.eof = 0; + + if (fseek (s->fp, offset, SEEK_SET)) + return CDK_File_Error; + return 0; +} + + +static cdk_error_t +stream_flush (cdk_stream_t s) +{ + assert (s); + + /* For some constellations it cannot be assured that the + return value is defined, thus we ignore it for now. */ + (void) fflush (s->fp); + return 0; +} + + +/** + * cdk_stream_set_armor_flag: + * @s: the stream object + * @type: the type of armor to use + * + * If the file is in read-mode, no armor type needs to be + * defined (armor_type=0) because the armor filter will be + * used for decoding existing armor data. + * For the write mode, @armor_type can be set to any valid + * armor type (message, key, sig). + **/ +cdk_error_t +cdk_stream_set_armor_flag (cdk_stream_t s, int armor_type) +{ + struct stream_filter_s *f; + + if (!s) + return CDK_Inv_Value; + f = filter_add (s, _cdk_filter_armor, fARMOR); + if (!f) + return CDK_Out_Of_Core; + f->u.afx.idx = f->u.afx.idx2 = armor_type; + f->ctl = stream_get_mode (s); + return 0; +} + + +/** + * cdk_stream_set_literal_flag: + * @s: the stream object + * @mode: the mode to use (binary, text, unicode) + * @fname: the file name to store in the packet. + * + * In read mode it kicks off the literal decoding routine to + * unwrap the data from the packet. The @mode parameter is ignored. + * In write mode the function can be used to wrap the stream data + * into a literal packet with the given mode and file name. + **/ +cdk_error_t +cdk_stream_set_literal_flag (cdk_stream_t s, cdk_lit_format_t mode, + const char *fname) +{ + struct stream_filter_s *f; + const char *orig_fname; + + _cdk_log_debug ("stream: enable literal mode.\n"); + + if (!s) + return CDK_Inv_Value; + + orig_fname = _cdk_stream_get_fname (s); + f = filter_add (s, _cdk_filter_literal, fLITERAL); + if (!f) + return CDK_Out_Of_Core; + f->u.pfx.mode = mode; + f->u.pfx.filename = fname ? cdk_strdup (fname) : NULL; + f->u.pfx.orig_filename = orig_fname ? cdk_strdup (orig_fname) : NULL; + f->ctl = stream_get_mode (s); + if (s->blkmode > 0) + { + f->u.pfx.blkmode.on = 1; + f->u.pfx.blkmode.size = s->blkmode; + } + return 0; +} + + +/** + * cdk_stream_set_cipher_flag: + * @s: the stream object + * @dek: the data encryption key + * @use_mdc: 1 means to use the MDC mode + * + * In read mode it kicks off the cipher filter to decrypt the data + * from the stream with the key given in @dek. + * In write mode the stream data will be encrypted with the DEK object + * and optionally, the @use_mdc parameter can be used to enable the MDC mode. + **/ +cdk_error_t +cdk_stream_set_cipher_flag (cdk_stream_t s, cdk_dek_t dek, int use_mdc) +{ + struct stream_filter_s *f; + + _cdk_log_debug ("stream: enable cipher mode\n"); + if (!s) + return CDK_Inv_Value; + f = filter_add (s, _cdk_filter_cipher, fCIPHER); + if (!f) + return CDK_Out_Of_Core; + dek->use_mdc = use_mdc; + f->ctl = stream_get_mode (s); + f->u.cfx.dek = dek; + f->u.cfx.mdc_method = use_mdc ? GCRY_MD_SHA1 : 0; + if (s->blkmode > 0) + { + f->u.cfx.blkmode.on = 1; + f->u.cfx.blkmode.size = s->blkmode; + } + return 0; +} + + +/** + * cdk_stream_set_compress_flag: + * @s: the stream object + * @algo: the compression algo + * @level: level of compression (0..9) + * + * In read mode it kicks off the decompression filter to retrieve + * the uncompressed data. + * In write mode the stream data will be compressed with the + * given algorithm at the given level. + **/ +cdk_error_t +cdk_stream_set_compress_flag (cdk_stream_t s, int algo, int level) +{ + struct stream_filter_s *f; + + if (!s) + return CDK_Inv_Value; + f = filter_add (s, _cdk_filter_compress, fCOMPRESS); + if (!f) + return CDK_Out_Of_Core; + f->ctl = stream_get_mode (s); + f->u.zfx.algo = algo; + f->u.zfx.level = level; + return 0; +} + + +/** + * cdk_stream_set_text_flag: + * @s: the stream object + * @lf: line ending + * + * Pushes the text filter to store the stream data in cannoncial format. + **/ +cdk_error_t +cdk_stream_set_text_flag (cdk_stream_t s, const char *lf) +{ + struct stream_filter_s *f; + + if (!s) + return CDK_Inv_Value; + f = filter_add (s, _cdk_filter_text, fTEXT); + if (!f) + return CDK_Out_Of_Core; + f->ctl = stream_get_mode (s); + f->u.tfx.lf = lf; + return 0; +} + + +/** + * cdk_stream_set_hash_flag: + * @s: the stream object + * @digest_algo: the digest algorithm to use + * + * This is for read-only streams. It pushes a digest filter to + * calculate the digest of the given stream data. + **/ +cdk_error_t +cdk_stream_set_hash_flag (cdk_stream_t s, int digest_algo) +{ + struct stream_filter_s *f; + + if (!s) + return CDK_Inv_Value; + if (stream_get_mode (s)) + return CDK_Inv_Mode; + f = filter_add (s, _cdk_filter_hash, fHASH); + if (!f) + return CDK_Out_Of_Core; + f->ctl = stream_get_mode (s); + f->u.mfx.digest_algo = digest_algo; + f->flags.rdonly = 1; + return 0; +} + + +/** + * cdk_stream_enable_cache: + * @s: the stream object + * @val: 1=on, 0=off + * + * Enable or disable the cache section of a stream object. + **/ +cdk_error_t +cdk_stream_enable_cache (cdk_stream_t s, int val) +{ + if (!s) + return CDK_Inv_Value; + if (!s->flags.write) + return CDK_Inv_Mode; + s->cache.on = val; + if (!s->cache.buf) + { + s->cache.buf = cdk_calloc (1, STREAM_BUFSIZE); + s->cache.alloced = STREAM_BUFSIZE; + _cdk_log_debug ("stream: allocate cache of %d octets\n", + STREAM_BUFSIZE); + } + return 0; +} + + +static int +stream_cache_flush (cdk_stream_t s, FILE * fp) +{ + int nwritten; + + assert (s); + + /* FIXME: We should find a way to use cdk_stream_write here. */ + if (s->cache.size > 0) + { + nwritten = fwrite (s->cache.buf, 1, s->cache.size, fp); + if (!nwritten) + return CDK_File_Error; + s->cache.size = 0; + s->cache.on = 0; + wipemem (s->cache.buf, s->cache.alloced); + } + return 0; +} + + +/** + * cdk_stream_kick_off: + * @inp: the input stream + * @out: the output stream. + * + * Passes the entire data from @inp into the output stream @out + * with all the activated filters. + */ +cdk_error_t +cdk_stream_kick_off (cdk_stream_t inp, cdk_stream_t out) +{ + byte buf[BUFSIZE]; + int nread, nwritten; + cdk_error_t rc; + + if (!inp || !out) + return CDK_Inv_Value; + rc = CDK_Success; + while (!cdk_stream_eof (inp)) + { + nread = cdk_stream_read (inp, buf, DIM (buf)); + if (!nread || nread == EOF) + break; + nwritten = cdk_stream_write (out, buf, nread); + if (!nwritten || nwritten == EOF) + { /* In case of errors, we leave the loop. */ + rc = inp->error; + break; + } + } + + wipemem (buf, sizeof (buf)); + return rc; +} + + +/** + * cdk_stream_mmap_part: + * @s: the stream + * @off: the offset where to start + * @len: how much bytes shall be mapped + * @ret_buf: the buffer to store the content + * @ret_buflen: length of the buffer + * + * Map the data of the given stream into a memory section. @ret_count + * contains the length of the buffer. + **/ +cdk_error_t +cdk_stream_mmap_part (cdk_stream_t s, off_t off, size_t len, + byte ** ret_buf, size_t * ret_buflen) +{ + off_t oldpos; + int n; + cdk_error_t rc; + + if (!s || !ret_buf || !ret_buflen) + return CDK_Inv_Value; + if (s->cbs_hd) + return CDK_Inv_Mode; + + *ret_buflen = 0; + *ret_buf = NULL; + oldpos = cdk_stream_tell (s); + rc = cdk_stream_flush (s); + if (!rc) + rc = cdk_stream_seek (s, off); + if (rc) + return rc; + if (!len) + len = cdk_stream_get_length (s); + if (!len || len > MAX_MAP_SIZE) + return 0; + *ret_buf = cdk_calloc (1, len + 1); + *ret_buflen = len; + n = cdk_stream_read (s, *ret_buf, len); + if (n != len) + *ret_buflen = n; + rc = cdk_stream_seek (s, oldpos); + return rc; +} + + +cdk_error_t +cdk_stream_mmap (cdk_stream_t inp, byte ** buf, size_t * buflen) +{ + off_t len; + + /* We need to make sure all data is flushed before we retrieve the size. */ + cdk_stream_flush (inp); + len = cdk_stream_get_length (inp); + return cdk_stream_mmap_part (inp, 0, len, buf, buflen); +} + + +/** + * cdk_stream_peek: + * @inp: the input stream handle + * @s: buffer + * @count: number of bytes to peek + * + * The function acts like cdk_stream_read with the difference that + * the file pointer is moved to the old position after the bytes were read. + **/ +int +cdk_stream_peek (cdk_stream_t inp, byte * buf, size_t buflen) +{ + off_t off; + int nbytes; + + if (!inp || !buf) + return 0; + if (inp->cbs_hd) + return 0; + + off = cdk_stream_tell (inp); + nbytes = cdk_stream_read (inp, buf, buflen); + if (nbytes == -1) + return 0; + if (cdk_stream_seek (inp, off)) + return 0; + return nbytes; +} + + +/* Try to read a line from the given stream. */ +int +_cdk_stream_gets (cdk_stream_t s, char *buf, size_t count) +{ + int c, i; + + assert (s); + + i = 0; + while (!cdk_stream_eof (s) && count > 0) + { + c = cdk_stream_getc (s); + if (c == EOF || c == '\r' || c == '\n') + { + buf[i++] = '\0'; + break; + } + buf[i++] = c; + count--; + } + return i; +} + + +/* Try to write string into the stream @s. */ +int +_cdk_stream_puts (cdk_stream_t s, const char *buf) +{ + return cdk_stream_write (s, buf, strlen (buf)); +} + + +/* Activate the block mode for the given stream. */ +cdk_error_t +_cdk_stream_set_blockmode (cdk_stream_t s, size_t nbytes) +{ + assert (s); + + _cdk_log_debug ("stream: activate block mode with blocksize %d\n", nbytes); + s->blkmode = nbytes; + return 0; +} + + +/* Return the block mode state of the given stream. */ +int +_cdk_stream_get_blockmode (cdk_stream_t s) +{ + return s ? s->blkmode : 0; +} diff --git a/src/daemon/https/opencdk/stream.h b/src/daemon/https/opencdk/stream.h new file mode 100644 index 00000000..df44c6c6 --- /dev/null +++ b/src/daemon/https/opencdk/stream.h @@ -0,0 +1,88 @@ +/* stream.h - internal definiton for the STREAM object + * Copyright (C) 2002, 2003, 2007 Timo Schulz + * + * This file is part of OpenCDK. + * + * OpenCDK 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. + * + * OpenCDK 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. + */ +#ifndef CDK_STREAM_H +#define CDK_STREAM_H + +/* The default buffer size for the stream. */ +#define STREAM_BUFSIZE 8192 + +enum { + fDUMMY = 0, + fARMOR = 1, + fCIPHER = 2, + fLITERAL = 3, + fCOMPRESS= 4, + fHASH = 5, + fTEXT = 6 +}; + +/* Type definition for the filter function. */ +typedef int (*filter_fnct_t) (void * opaque, int ctl, FILE * in, FILE * out); + +/* The stream filter context structure. */ +struct stream_filter_s +{ + struct stream_filter_s *next; + filter_fnct_t fnct; + void *opaque; + FILE *tmp; + union { + armor_filter_t afx; + cipher_filter_t cfx; + literal_filter_t pfx; + compress_filter_t zfx; + text_filter_t tfx; + md_filter_t mfx; + } u; + struct { + unsigned enabled:1; + unsigned rdonly:1; + unsigned error:1; + } flags; + unsigned type; + unsigned ctl; +}; + + +/* The stream context structure. */ +struct cdk_stream_s { + struct stream_filter_s *filters; + int fmode; + int error; + size_t blkmode; + struct { + unsigned filtrated:1; + unsigned eof:1; + unsigned write:1; + unsigned temp:1; + unsigned reset:1; + unsigned no_filter:1; + unsigned compressed:3; + } flags; + struct { + unsigned char *buf; + unsigned on:1; + size_t size; + size_t alloced; + } cache; + char *fname; + FILE *fp; + unsigned int fp_ref:1; + struct cdk_stream_cbs_s cbs; + void *cbs_hd; +}; + +#endif /* CDK_STREAM_H */ diff --git a/src/daemon/https/opencdk/types.h b/src/daemon/https/opencdk/types.h new file mode 100644 index 00000000..c53a0850 --- /dev/null +++ b/src/daemon/https/opencdk/types.h @@ -0,0 +1,44 @@ +/* types.h - Some type definitions + * Copyright (C) 2002, 2003, 2007 Timo Schulz + * + * This file is part of OpenCDK. + * + * OpenCDK 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. + * + * OpenCDK 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. + */ +#ifndef CDK_TYPES_H +#define CDK_TYPES_H + +#include + +#ifndef HAVE_BYTE_TYPEDEF +# undef byte + typedef unsigned char byte; +# define HAVE_BYTE_TYPEDEF +#endif + +#ifndef HAVE_U16_TYPEDEF +# undef u16 + typedef unsigned short u16; +# define HAVE_U16_TYPEDEF +#endif + +#ifndef HAVE_U32_TYPEDEF +# undef u32 + typedef unsigned int u32; +# define HAVE_U32_TYPEDEF +#endif + +#ifndef DIM +# define DIM(v) (sizeof (v)/sizeof ((v)[0])) +# define DIMof(type, member) DIM(((type *)0)->member) +#endif + +#endif /* CDK_TYPES_H */ diff --git a/src/daemon/https/opencdk/verify.c b/src/daemon/https/opencdk/verify.c new file mode 100644 index 00000000..71b4c237 --- /dev/null +++ b/src/daemon/https/opencdk/verify.c @@ -0,0 +1,306 @@ +/* verify.c - Verify signatures + * Copyright (C) 2001, 2002, 2003, 2007 Timo Schulz + * + * This file is part of OpenCDK. + * + * OpenCDK 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. + * + * OpenCDK 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. + */ +#ifdef HAVE_CONFIG_H +# include +#endif +#include +#include +#include +#include + +#include "opencdk.h" +#include "main.h" +#include "filters.h" +#include "packet.h" + + +/* Table of all supported digest algorithms and their names. */ +struct +{ + const char *name; + int algo; +} digest_table[] = +{ + { + "MD5", GCRY_MD_MD5}, + { + "SHA1", GCRY_MD_SHA1}, + { + "RIPEMD160", GCRY_MD_RMD160}, + { + "SHA256", GCRY_MD_SHA256}, + { + "SHA384", GCRY_MD_SHA384}, + { + "SHA512", GCRY_MD_SHA512}, + { + NULL, 0} +}; + + +static int file_verify_clearsign (cdk_ctx_t, const char *, const char *); + + +/** + * cdk_stream_verify: + * @hd: session handle + * @inp: the input stream + * @data: for detached signatures, this is the data stream @inp is the sig + * @out: where the output shall be written. + */ +cdk_error_t +cdk_stream_verify (cdk_ctx_t hd, cdk_stream_t inp, cdk_stream_t data, + cdk_stream_t out) +{ + /* FIXME: out is not currently used. */ + if (cdk_armor_filter_use (inp)) + cdk_stream_set_armor_flag (inp, 0); + return _cdk_proc_packets (hd, inp, data, NULL, NULL, NULL); +} + + +/** + * cdk_file_verify: + * @hd: the session handle + * @file: the input file + * @data_file: for detached signature this is the data file and @file is the sig. + * @output: the output file + * + * Verify a signature. + **/ +cdk_error_t +cdk_file_verify (cdk_ctx_t hd, const char *file, const char *data_file, + const char *output) +{ + struct stat stbuf; + cdk_stream_t inp, data; + char buf[4096]; + int n; + cdk_error_t rc; + + if (!hd || !file) + return CDK_Inv_Value; + if (output && !hd->opt.overwrite && !stat (output, &stbuf)) + return CDK_Inv_Mode; + + rc = cdk_stream_open (file, &inp); + if (rc) + return rc; + if (cdk_armor_filter_use (inp)) + { + n = cdk_stream_peek (inp, (byte *) buf, DIM (buf) - 1); + if (!n || n == -1) + return CDK_EOF; + buf[n] = '\0'; + if (strstr (buf, "BEGIN PGP SIGNED MESSAGE")) + { + cdk_stream_close (inp); + return file_verify_clearsign (hd, file, output); + } + cdk_stream_set_armor_flag (inp, 0); + } + + if (data_file) + { + rc = cdk_stream_open (data_file, &data); + if (rc) + { + cdk_stream_close (inp); + return rc; + } + } + else + data = NULL; + + rc = _cdk_proc_packets (hd, inp, data, NULL, NULL, NULL); + + if (data != NULL) + cdk_stream_close (data); + cdk_stream_close (inp); + return rc; +} + + +void +_cdk_result_verify_free (cdk_verify_result_t res) +{ + if (!res) + return; + cdk_free (res->policy_url); + cdk_free (res->sig_data); + cdk_free (res); +} + + +cdk_verify_result_t +_cdk_result_verify_new (void) +{ + cdk_verify_result_t res; + + res = cdk_calloc (1, sizeof *res); + if (!res) + return NULL; + return res; +} + + +static cdk_error_t +file_verify_clearsign (cdk_ctx_t hd, const char *file, const char *output) +{ + cdk_stream_t inp = NULL, out = NULL, tmp = NULL; + gcry_md_hd_t md = NULL; + char buf[512], chk[512]; + const char *s; + int i, is_signed = 0, nbytes; + int digest_algo = 0; + gcry_error_t err; + cdk_error_t rc; + + if (output) + { + rc = cdk_stream_create (output, &out); + if (rc) + return rc; + } + + rc = cdk_stream_open (file, &inp); + if (rc) + { + if (output) + cdk_stream_close (out); + return rc; + } + + s = "-----BEGIN PGP SIGNED MESSAGE-----"; + while (!cdk_stream_eof (inp)) + { + nbytes = _cdk_stream_gets (inp, buf, DIM (buf) - 1); + if (!nbytes || nbytes == -1) + break; + if (!strncmp (buf, s, strlen (s))) + { + is_signed = 1; + break; + } + } + + if (cdk_stream_eof (inp) && !is_signed) + { + rc = CDK_Armor_Error; + goto leave; + } + + while (!cdk_stream_eof (inp)) + { + nbytes = _cdk_stream_gets (inp, buf, DIM (buf) - 1); + if (!nbytes || nbytes == -1) + break; + if (nbytes == 1) /* Empty line */ + break; + else if (!strncmp (buf, "Hash: ", 6)) + { + for (i = 0; digest_table[i].name; i++) + { + if (!strcmp (buf + 6, digest_table[i].name)) + { + digest_algo = digest_table[i].algo; + break; + } + } + } + } + + if (digest_algo && gcry_md_test_algo (digest_algo)) + { + rc = CDK_Inv_Algo; + goto leave; + } + + if (!digest_algo) + digest_algo = GCRY_MD_MD5; + + err = gcry_md_open (&md, digest_algo, 0); + if (err) + { + rc = map_gcry_error (err); + goto leave; + } + + s = "-----BEGIN PGP SIGNATURE-----"; + while (!cdk_stream_eof (inp)) + { + nbytes = _cdk_stream_gets (inp, buf, DIM (buf) - 1); + if (!nbytes || nbytes == -1) + break; + if (!strncmp (buf, s, strlen (s))) + break; + else + { + cdk_stream_peek (inp, (byte *) chk, DIM (chk) - 1); + i = strncmp (chk, s, strlen (s)); + if (strlen (buf) == 0 && i == 0) + continue; /* skip last '\n' */ + _cdk_trim_string (buf, i == 0 ? 0 : 1); + gcry_md_write (md, buf, strlen (buf)); + } + if (!strncmp (buf, "- ", 2)) /* FIXME: handle it recursive. */ + memmove (buf, buf + 2, nbytes - 2); + if (out) + { + if (strstr (buf, "\r\n")) + buf[strlen (buf) - 2] = '\0'; + cdk_stream_write (out, buf, strlen (buf)); + _cdk_stream_puts (out, _cdk_armor_get_lineend ()); + } + } + + /* We create a temporary stream object to store the + signature data in there. */ + rc = cdk_stream_tmp_new (&tmp); + if (rc) + goto leave; + + s = "-----BEGIN PGP SIGNATURE-----\n"; + _cdk_stream_puts (tmp, s); + while (!cdk_stream_eof (inp)) + { + nbytes = _cdk_stream_gets (inp, buf, DIM (buf) - 1); + if (!nbytes || nbytes == -1) + break; + if (nbytes < (DIM (buf) - 3)) + { + buf[nbytes - 1] = '\n'; + buf[nbytes] = '\0'; + } + cdk_stream_write (tmp, buf, nbytes); + } + + /* FIXME: This code is not very elegant. */ + cdk_stream_tmp_set_mode (tmp, STREAMCTL_READ); + cdk_stream_seek (tmp, 0); + cdk_stream_set_armor_flag (tmp, 0); + cdk_stream_read (tmp, NULL, 0); + + /* the digest handle will be closed there. */ + rc = _cdk_proc_packets (hd, tmp, NULL, NULL, NULL, md); + +leave: + gcry_md_close (md); + cdk_stream_close (out); + cdk_stream_close (tmp); + cdk_stream_close (inp); + return rc; +} diff --git a/src/daemon/https/opencdk/write-packet.c b/src/daemon/https/opencdk/write-packet.c new file mode 100644 index 00000000..46d239dd --- /dev/null +++ b/src/daemon/https/opencdk/write-packet.c @@ -0,0 +1,947 @@ +/* write-packet.c - Write OpenPGP packets + * Copyright (C) 2001, 2002, 2003, 2007 Timo Schulz + * + * This file is part of OpenCDK. + * + * OpenCDK 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. + * + * OpenCDK 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. + */ +#ifdef HAVE_CONFIG_H +#include +#endif +#include +#include +#include + +#include "opencdk.h" +#include "main.h" + + +static int +stream_write (cdk_stream_t s, const void *buf, size_t buflen) +{ + int nwritten; + + nwritten = cdk_stream_write (s, buf, buflen); + if (nwritten == EOF) + return _cdk_stream_get_errno (s); + return 0; +} + + +static int +stream_read (cdk_stream_t s, void *buf, size_t buflen, size_t * r_nread) +{ + int nread; + + assert (r_nread); + + nread = cdk_stream_read (s, buf, buflen); + if (nread == EOF) + return _cdk_stream_get_errno (s); + *r_nread = nread; + return 0; +} + + +static int +stream_putc (cdk_stream_t s, int c) +{ + int nwritten = cdk_stream_putc (s, c); + if (nwritten == EOF) + return _cdk_stream_get_errno (s); + return 0; +} + + +static int +write_32 (cdk_stream_t out, u32 u) +{ + byte buf[4]; + + buf[0] = u >> 24; + buf[1] = u >> 16; + buf[2] = u >> 8; + buf[3] = u; + return stream_write (out, buf, 4); +} + + +static int +write_16 (cdk_stream_t out, u16 u) +{ + byte buf[2]; + + buf[0] = u >> 8; + buf[1] = u; + return stream_write (out, buf, 2); +} + + +static size_t +calc_mpisize (gcry_mpi_t mpi[MAX_CDK_PK_PARTS], size_t ncount) +{ + size_t size, i; + + size = 0; + for (i = 0; i < ncount; i++) + size += (gcry_mpi_get_nbits (mpi[i]) + 7) / 8 + 2; + return size; +} + + +static int +write_mpi (cdk_stream_t out, gcry_mpi_t m) +{ + byte buf[MAX_MPI_BYTES + 2]; + size_t nbits, nread; + gcry_error_t err; + + if (!out || !m) + return CDK_Inv_Value; + nbits = gcry_mpi_get_nbits (m); + if (nbits > MAX_MPI_BITS || nbits < 1) + return CDK_MPI_Error; + err = gcry_mpi_print (GCRYMPI_FMT_PGP, buf, MAX_MPI_BYTES + 2, &nread, m); + if (err) + return map_gcry_error (err); + return stream_write (out, buf, nread); +} + + +static cdk_error_t +write_mpibuf (cdk_stream_t out, gcry_mpi_t mpi[MAX_CDK_PK_PARTS], + size_t count) +{ + size_t i; + cdk_error_t rc; + + for (i = 0; i < count; i++) + { + rc = write_mpi (out, mpi[i]); + if (rc) + return rc; + } + return 0; +} + + +static cdk_error_t +pkt_encode_len (cdk_stream_t out, size_t pktlen) +{ + cdk_error_t rc; + + assert (out); + + rc = 0; + if (!pktlen) + { + /* Block mode, partial bodies, with 'DEF_BLOCKSIZE' from main.h */ + rc = stream_putc (out, (0xE0 | DEF_BLOCKBITS)); + } + else if (pktlen < 192) + rc = stream_putc (out, pktlen); + else if (pktlen < 8384) + { + pktlen -= 192; + rc = stream_putc (out, (pktlen / 256) + 192); + if (!rc) + rc = stream_putc (out, (pktlen % 256)); + } + else + { + rc = stream_putc (out, 255); + if (!rc) + rc = write_32 (out, pktlen); + } + + return rc; +} + + +static cdk_error_t +write_head_new (cdk_stream_t out, size_t size, int type) +{ + cdk_error_t rc; + + assert (out); + + if (type < 0 || type > 63) + return CDK_Inv_Packet; + rc = stream_putc (out, (0xC0 | type)); + if (!rc) + rc = pkt_encode_len (out, size); + return rc; +} + + +static cdk_error_t +write_head_old (cdk_stream_t out, size_t size, int type) +{ + cdk_error_t rc; + int ctb; + + assert (out); + + if (type < 0 || type > 16) + return CDK_Inv_Packet; + ctb = 0x80 | (type << 2); + if (!size) + ctb |= 3; + else if (size < 256) + ; + else if (size < 65536) + ctb |= 1; + else + ctb |= 2; + rc = stream_putc (out, ctb); + if (!size) + return rc; + if (!rc) + { + if (size < 256) + rc = stream_putc (out, size); + else if (size < 65536) + rc = write_16 (out, size); + else + rc = write_32 (out, size); + } + + return rc; +} + + +/* Write special PGP2 packet header. PGP2 (wrongly) uses two byte header + length for signatures and keys even if the size is < 256. */ +static cdk_error_t +pkt_write_head2 (cdk_stream_t out, size_t size, int type) +{ + cdk_error_t rc; + + rc = cdk_stream_putc (out, 0x80 | (type << 2) | 1); + if (!rc) + rc = cdk_stream_putc (out, size >> 8); + if (!rc) + rc = cdk_stream_putc (out, size & 0xff); + return rc; +} + + +static int +pkt_write_head (cdk_stream_t out, int old_ctb, size_t size, int type) +{ + if (old_ctb) + return write_head_old (out, size, type); + return write_head_new (out, size, type); +} + + +static cdk_error_t +write_encrypted (cdk_stream_t out, cdk_pkt_encrypted_t enc, int old_ctb) +{ + size_t nbytes; + cdk_error_t rc; + + assert (out); + assert (enc); + + if (DEBUG_PKT) + _cdk_log_debug ("write_encrypted: %lu bytes\n", enc->len); + + nbytes = enc->len ? (enc->len + enc->extralen) : 0; + rc = pkt_write_head (out, old_ctb, nbytes, CDK_PKT_ENCRYPTED); + /* The rest of the packet is ciphertext */ + return rc; +} + + +static int +write_encrypted_mdc (cdk_stream_t out, cdk_pkt_encrypted_t enc) +{ + size_t nbytes; + cdk_error_t rc; + + assert (out); + assert (enc); + + if (!enc->mdc_method) + return CDK_Inv_Packet; + + if (DEBUG_PKT) + _cdk_log_debug ("write_encrypted_mdc: %lu bytes\n", enc->len); + + nbytes = enc->len ? (enc->len + enc->extralen + 1) : 0; + rc = pkt_write_head (out, 0, nbytes, CDK_PKT_ENCRYPTED_MDC); + if (!rc) + rc = stream_putc (out, 1); /* version */ + /* The rest of the packet is ciphertext */ + return rc; +} + + +static cdk_error_t +write_symkey_enc (cdk_stream_t out, cdk_pkt_symkey_enc_t ske) +{ + cdk_s2k_t s2k; + size_t size = 0, s2k_size = 0; + cdk_error_t rc; + + assert (out); + assert (ske); + + if (ske->version != 4) + return CDK_Inv_Packet; + + if (DEBUG_PKT) + _cdk_log_debug ("write_symkey_enc:\n"); + + s2k = ske->s2k; + if (s2k->mode == CDK_S2K_SALTED || s2k->mode == CDK_S2K_ITERSALTED) + s2k_size = 8; + if (s2k->mode == CDK_S2K_ITERSALTED) + s2k_size++; + size = 4 + s2k_size + ske->seskeylen; + rc = pkt_write_head (out, 0, size, CDK_PKT_SYMKEY_ENC); + if (!rc) + rc = stream_putc (out, ske->version); + if (!rc) + rc = stream_putc (out, ske->cipher_algo); + if (!rc) + rc = stream_putc (out, s2k->mode); + if (!rc) + rc = stream_putc (out, s2k->hash_algo); + if (s2k->mode == CDK_S2K_SALTED || s2k->mode == CDK_S2K_ITERSALTED) + { + rc = stream_write (out, s2k->salt, 8); + if (!rc) + { + if (s2k->mode == CDK_S2K_ITERSALTED) + rc = stream_putc (out, s2k->count); + } + } + return rc; +} + + +static int +write_pubkey_enc (cdk_stream_t out, cdk_pkt_pubkey_enc_t pke, int old_ctb) +{ + size_t size; + int rc, nenc; + + assert (out); + assert (pke); + + if (pke->version < 2 || pke->version > 3) + return CDK_Inv_Packet; + if (!KEY_CAN_ENCRYPT (pke->pubkey_algo)) + return CDK_Inv_Algo; + + if (DEBUG_PKT) + _cdk_log_debug ("write_pubkey_enc:\n"); + + nenc = cdk_pk_get_nenc (pke->pubkey_algo); + size = 10 + calc_mpisize (pke->mpi, nenc); + rc = pkt_write_head (out, old_ctb, size, CDK_PKT_PUBKEY_ENC); + if (rc) + return rc; + + rc = stream_putc (out, pke->version); + if (!rc) + rc = write_32 (out, pke->keyid[0]); + if (!rc) + rc = write_32 (out, pke->keyid[1]); + if (!rc) + rc = stream_putc (out, pke->pubkey_algo); + if (!rc) + rc = write_mpibuf (out, pke->mpi, nenc); + return rc; +} + + +static cdk_error_t +write_mdc (cdk_stream_t out, cdk_pkt_mdc_t mdc) +{ + cdk_error_t rc; + + assert (mdc); + assert (out); + + if (DEBUG_PKT) + _cdk_log_debug ("write_mdc:\n"); + + /* This packet requires a fixed header encoding */ + rc = stream_putc (out, 0xD3); /* packet ID and 1 byte length */ + if (!rc) + rc = stream_putc (out, 0x14); + if (!rc) + rc = stream_write (out, mdc->hash, DIM (mdc->hash)); + return rc; +} + + +static size_t +calc_subpktsize (cdk_subpkt_t s) +{ + size_t nbytes; + + /* In the count mode, no buffer is returned. */ + _cdk_subpkt_get_array (s, 1, &nbytes); + return nbytes; +} + + +static cdk_error_t +write_v3_sig (cdk_stream_t out, cdk_pkt_signature_t sig, int nsig) +{ + size_t size; + cdk_error_t rc; + + size = 19 + calc_mpisize (sig->mpi, nsig); + if (is_RSA (sig->pubkey_algo)) + rc = pkt_write_head2 (out, size, CDK_PKT_SIGNATURE); + else + rc = pkt_write_head (out, 1, size, CDK_PKT_SIGNATURE); + if (!rc) + rc = stream_putc (out, sig->version); + if (!rc) + rc = stream_putc (out, 5); + if (!rc) + rc = stream_putc (out, sig->sig_class); + if (!rc) + rc = write_32 (out, sig->timestamp); + if (!rc) + rc = write_32 (out, sig->keyid[0]); + if (!rc) + rc = write_32 (out, sig->keyid[1]); + if (!rc) + rc = stream_putc (out, sig->pubkey_algo); + if (!rc) + rc = stream_putc (out, sig->digest_algo); + if (!rc) + rc = stream_putc (out, sig->digest_start[0]); + if (!rc) + rc = stream_putc (out, sig->digest_start[1]); + if (!rc) + rc = write_mpibuf (out, sig->mpi, nsig); + return rc; +} + + +static cdk_error_t +write_signature (cdk_stream_t out, cdk_pkt_signature_t sig, int old_ctb) +{ + byte *buf; + size_t nbytes, size, nsig; + cdk_error_t rc; + + assert (out); + assert (sig); + + if (!KEY_CAN_SIGN (sig->pubkey_algo)) + return CDK_Inv_Algo; + if (sig->version < 2 || sig->version > 4) + return CDK_Inv_Packet; + + if (DEBUG_PKT) + _cdk_log_debug ("write_signature:\n"); + + nsig = cdk_pk_get_nsig (sig->pubkey_algo); + if (!nsig) + return CDK_Inv_Algo; + if (sig->version < 4) + return write_v3_sig (out, sig, nsig); + + size = 10 + calc_subpktsize (sig->hashed) + + calc_subpktsize (sig->unhashed) + calc_mpisize (sig->mpi, nsig); + rc = pkt_write_head (out, 0, size, CDK_PKT_SIGNATURE); + if (!rc) + rc = stream_putc (out, 4); + if (!rc) + rc = stream_putc (out, sig->sig_class); + if (!rc) + rc = stream_putc (out, sig->pubkey_algo); + if (!rc) + rc = stream_putc (out, sig->digest_algo); + if (!rc) + rc = write_16 (out, sig->hashed_size); + if (!rc) + { + buf = _cdk_subpkt_get_array (sig->hashed, 0, &nbytes); + if (!buf) + return CDK_Out_Of_Core; + rc = stream_write (out, buf, nbytes); + cdk_free (buf); + } + if (!rc) + rc = write_16 (out, sig->unhashed_size); + if (!rc) + { + buf = _cdk_subpkt_get_array (sig->unhashed, 0, &nbytes); + if (!buf) + return CDK_Out_Of_Core; + rc = stream_write (out, buf, nbytes); + cdk_free (buf); + } + if (!rc) + rc = stream_putc (out, sig->digest_start[0]); + if (!rc) + rc = stream_putc (out, sig->digest_start[1]); + if (!rc) + rc = write_mpibuf (out, sig->mpi, nsig); + return rc; +} + + +static cdk_error_t +write_public_key (cdk_stream_t out, cdk_pkt_pubkey_t pk, + int is_subkey, int old_ctb) +{ + int pkttype, ndays = 0; + size_t npkey = 0, size = 6; + cdk_error_t rc; + + assert (out); + assert (pk); + + if (pk->version < 2 || pk->version > 4) + return CDK_Inv_Packet; + + if (DEBUG_PKT) + _cdk_log_debug ("write_public_key: subkey=%d\n", is_subkey); + + pkttype = is_subkey ? CDK_PKT_PUBLIC_SUBKEY : CDK_PKT_PUBLIC_KEY; + npkey = cdk_pk_get_npkey (pk->pubkey_algo); + if (!npkey) + return CDK_Inv_Algo; + if (pk->version < 4) + size += 2; /* expire date */ + if (is_subkey) + old_ctb = 0; + size += calc_mpisize (pk->mpi, npkey); + if (old_ctb) + rc = pkt_write_head2 (out, size, pkttype); + else + rc = pkt_write_head (out, old_ctb, size, pkttype); + if (!rc) + rc = stream_putc (out, pk->version); + if (!rc) + rc = write_32 (out, pk->timestamp); + if (!rc && pk->version < 4) + { + if (pk->expiredate) + ndays = (u16) ((pk->expiredate - pk->timestamp) / 86400L); + rc = write_16 (out, ndays); + } + if (!rc) + rc = stream_putc (out, pk->pubkey_algo); + if (!rc) + rc = write_mpibuf (out, pk->mpi, npkey); + return rc; +} + + +static int +calc_s2ksize (cdk_pkt_seckey_t sk) +{ + size_t nbytes = 0; + + if (!sk->is_protected) + return 0; + switch (sk->protect.s2k->mode) + { + case CDK_S2K_SIMPLE: + nbytes = 2; + break; + case CDK_S2K_SALTED: + nbytes = 10; + break; + case CDK_S2K_ITERSALTED: + nbytes = 11; + break; + } + nbytes += sk->protect.ivlen; + nbytes++; /* single cipher byte */ + return nbytes; +} + + +static cdk_error_t +write_secret_key (cdk_stream_t out, cdk_pkt_seckey_t sk, + int is_subkey, int old_ctb) +{ + cdk_pkt_pubkey_t pk = NULL; + size_t size = 6, npkey, nskey; + int pkttype, s2k_mode; + cdk_error_t rc; + + assert (out); + assert (sk); + + if (!sk->pk) + return CDK_Inv_Value; + pk = sk->pk; + if (pk->version < 2 || pk->version > 4) + return CDK_Inv_Packet; + + if (DEBUG_PKT) + _cdk_log_debug ("write_secret_key:\n"); + + npkey = cdk_pk_get_npkey (pk->pubkey_algo); + nskey = cdk_pk_get_nskey (pk->pubkey_algo); + if (!npkey || !nskey) + return CDK_Inv_Algo; + if (pk->version < 4) + size += 2; + /* If the key is unprotected, the 1 extra byte: + 1 octet - cipher algorithm byte (0x00) + the other bytes depend on the mode: + a) simple checksum - 2 octets + b) sha-1 checksum - 20 octets */ + size = !sk->is_protected ? size + 1 : size + 1 + calc_s2ksize (sk); + size += calc_mpisize (pk->mpi, npkey); + if (sk->version == 3 || !sk->is_protected) + { + if (sk->version == 3) + { + size += 2; /* force simple checksum */ + sk->protect.sha1chk = 0; + } + else + size += sk->protect.sha1chk ? 20 : 2; + size += calc_mpisize (sk->mpi, nskey); + } + else /* We do not know anything about the encrypted mpi's so we + treat the data as opaque. */ + size += sk->enclen; + + pkttype = is_subkey ? CDK_PKT_SECRET_SUBKEY : CDK_PKT_SECRET_KEY; + rc = pkt_write_head (out, old_ctb, size, pkttype); + if (!rc) + rc = stream_putc (out, pk->version); + if (!rc) + rc = write_32 (out, pk->timestamp); + if (!rc && pk->version < 4) + { + u16 ndays = 0; + if (pk->expiredate) + ndays = (u16) ((pk->expiredate - pk->timestamp) / 86400L); + rc = write_16 (out, ndays); + } + if (!rc) + rc = stream_putc (out, pk->pubkey_algo); + if (!rc) + rc = write_mpibuf (out, pk->mpi, npkey); + if (sk->is_protected == 0) + rc = stream_putc (out, 0x00); + else + { + if (is_RSA (pk->pubkey_algo) && pk->version < 4) + stream_putc (out, sk->protect.algo); + else if (sk->protect.s2k) + { + s2k_mode = sk->protect.s2k->mode; + rc = stream_putc (out, sk->protect.sha1chk ? 0xFE : 0xFF); + if (!rc) + rc = stream_putc (out, sk->protect.algo); + if (!rc) + rc = stream_putc (out, sk->protect.s2k->mode); + if (!rc) + rc = stream_putc (out, sk->protect.s2k->hash_algo); + if (!rc && (s2k_mode == 1 || s2k_mode == 3)) + { + rc = stream_write (out, sk->protect.s2k->salt, 8); + if (!rc && s2k_mode == 3) + rc = stream_putc (out, sk->protect.s2k->count); + } + } + else + return CDK_Inv_Value; + rc = stream_write (out, sk->protect.iv, sk->protect.ivlen); + } + if (!rc && sk->is_protected && pk->version == 4) + { + if (sk->encdata && sk->enclen) + rc = stream_write (out, sk->encdata, sk->enclen); + } + else + { + if (!rc) + rc = write_mpibuf (out, sk->mpi, nskey); + if (!rc) + { + if (!sk->csum) + sk->csum = _cdk_sk_get_csum (sk); + rc = write_16 (out, sk->csum); + } + } + + return rc; +} + + +static cdk_error_t +write_compressed (cdk_stream_t out, cdk_pkt_compressed_t cd) +{ + cdk_error_t rc; + + assert (out); + assert (cd); + + if (DEBUG_PKT) + _cdk_log_debug ("packet: write_compressed\n"); + + /* Use an old (RFC1991) header for this packet. */ + rc = pkt_write_head (out, 1, 0, CDK_PKT_COMPRESSED); + if (!rc) + rc = stream_putc (out, cd->algorithm); + return rc; +} + + +static cdk_error_t +write_literal (cdk_stream_t out, cdk_pkt_literal_t pt, int old_ctb) +{ + byte buf[BUFSIZE]; + size_t size; + cdk_error_t rc; + + assert (out); + assert (pt); + + /* We consider a packet without a body as an invalid packet. + At least one octet must be present. */ + if (!pt->len) + return CDK_Inv_Packet; + + if (DEBUG_PKT) + _cdk_log_debug ("write_literal:\n"); + + size = 6 + pt->namelen + pt->len; + rc = pkt_write_head (out, old_ctb, size, CDK_PKT_LITERAL); + if (rc) + return rc; + + rc = stream_putc (out, pt->mode); + if (rc) + return rc; + rc = stream_putc (out, pt->namelen); + if (rc) + return rc; + + if (pt->namelen > 0) + rc = stream_write (out, pt->name, pt->namelen); + if (!rc) + rc = write_32 (out, pt->timestamp); + if (rc) + return rc; + + while (!cdk_stream_eof (pt->buf) && !rc) + { + rc = stream_read (pt->buf, buf, DIM (buf), &size); + if (!rc) + rc = stream_write (out, buf, size); + } + + wipemem (buf, sizeof (buf)); + return rc; +} + + +static cdk_error_t +write_onepass_sig (cdk_stream_t out, cdk_pkt_onepass_sig_t sig) +{ + cdk_error_t rc; + + assert (out); + assert (sig); + + if (sig->version != 3) + return CDK_Inv_Packet; + + if (DEBUG_PKT) + _cdk_log_debug ("write_onepass_sig:\n"); + + rc = pkt_write_head (out, 0, 13, CDK_PKT_ONEPASS_SIG); + if (!rc) + rc = stream_putc (out, sig->version); + if (!rc) + rc = stream_putc (out, sig->sig_class); + if (!rc) + rc = stream_putc (out, sig->digest_algo); + if (!rc) + rc = stream_putc (out, sig->pubkey_algo); + if (!rc) + rc = write_32 (out, sig->keyid[0]); + if (!rc) + rc = write_32 (out, sig->keyid[1]); + if (!rc) + rc = stream_putc (out, sig->last); + return rc; +} + + +static cdk_error_t +write_user_id (cdk_stream_t out, cdk_pkt_userid_t id, int old_ctb, + int pkttype) +{ + cdk_error_t rc; + + if (!out || !id) + return CDK_Inv_Value; + + if (pkttype == CDK_PKT_ATTRIBUTE) + { + if (!id->attrib_img) + return CDK_Inv_Value; + rc = + pkt_write_head (out, old_ctb, id->attrib_len + 6, CDK_PKT_ATTRIBUTE); + if (rc) + return rc; + /* Write subpacket part. */ + stream_putc (out, 255); + write_32 (out, id->attrib_len + 1); + stream_putc (out, 1); + rc = stream_write (out, id->attrib_img, id->attrib_len); + } + else + { + if (!id->name) + return CDK_Inv_Value; + rc = pkt_write_head (out, old_ctb, id->len, CDK_PKT_USER_ID); + if (!rc) + rc = stream_write (out, id->name, id->len); + } + + return rc; +} + + +/** + * cdk_pkt_write: + * @out: the output stream handle + * @pkt: the packet itself + * + * Write the contents of @pkt into the @out stream. + * Return 0 on success. + **/ +cdk_error_t +cdk_pkt_write (cdk_stream_t out, cdk_packet_t pkt) +{ + cdk_error_t rc; + + if (!out || !pkt) + return CDK_Inv_Value; + + _cdk_log_debug ("write packet pkttype=%d\n", pkt->pkttype); + switch (pkt->pkttype) + { + case CDK_PKT_LITERAL: + rc = write_literal (out, pkt->pkt.literal, pkt->old_ctb); + break; + case CDK_PKT_ONEPASS_SIG: + rc = write_onepass_sig (out, pkt->pkt.onepass_sig); + break; + case CDK_PKT_MDC: + rc = write_mdc (out, pkt->pkt.mdc); + break; + case CDK_PKT_SYMKEY_ENC: + rc = write_symkey_enc (out, pkt->pkt.symkey_enc); + break; + case CDK_PKT_ENCRYPTED: + rc = write_encrypted (out, pkt->pkt.encrypted, pkt->old_ctb); + break; + case CDK_PKT_ENCRYPTED_MDC: + rc = write_encrypted_mdc (out, pkt->pkt.encrypted); + break; + case CDK_PKT_PUBKEY_ENC: + rc = write_pubkey_enc (out, pkt->pkt.pubkey_enc, pkt->old_ctb); + break; + case CDK_PKT_SIGNATURE: + rc = write_signature (out, pkt->pkt.signature, pkt->old_ctb); + break; + case CDK_PKT_PUBLIC_KEY: + rc = write_public_key (out, pkt->pkt.public_key, 0, pkt->old_ctb); + break; + case CDK_PKT_PUBLIC_SUBKEY: + rc = write_public_key (out, pkt->pkt.public_key, 1, pkt->old_ctb); + break; + case CDK_PKT_COMPRESSED: + rc = write_compressed (out, pkt->pkt.compressed); + break; + case CDK_PKT_SECRET_KEY: + rc = write_secret_key (out, pkt->pkt.secret_key, 0, pkt->old_ctb); + break; + case CDK_PKT_SECRET_SUBKEY: + rc = write_secret_key (out, pkt->pkt.secret_key, 1, pkt->old_ctb); + break; + case CDK_PKT_USER_ID: + case CDK_PKT_ATTRIBUTE: + rc = write_user_id (out, pkt->pkt.user_id, pkt->old_ctb, pkt->pkttype); + break; + default: + rc = CDK_Inv_Packet; + break; + } + + if (DEBUG_PKT) + _cdk_log_debug ("write_packet rc=%d pkttype=%d\n", rc, pkt->pkttype); + return rc; +} + + +cdk_error_t +_cdk_pkt_write2 (cdk_stream_t out, int pkttype, void *pktctx) +{ + cdk_packet_t pkt; + cdk_error_t rc; + + rc = cdk_pkt_new (&pkt); + if (rc) + return rc; + + switch (pkttype) + { + case CDK_PKT_PUBLIC_KEY: + case CDK_PKT_PUBLIC_SUBKEY: + pkt->pkt.public_key = pktctx; + break; + case CDK_PKT_SIGNATURE: + pkt->pkt.signature = pktctx; + break; + case CDK_PKT_SECRET_KEY: + case CDK_PKT_SECRET_SUBKEY: + pkt->pkt.secret_key = pktctx; + break; + + case CDK_PKT_USER_ID: + pkt->pkt.user_id = pktctx; + break; + } + pkt->pkttype = pkttype; + rc = cdk_pkt_write (out, pkt); + cdk_free (pkt); + return rc; +} + + +cdk_error_t +_cdk_pkt_write_fp (FILE * out, cdk_packet_t pkt) +{ + cdk_stream_t so; + cdk_error_t rc; + + rc = _cdk_stream_fpopen (out, 1, &so); + if (rc) + return rc; + rc = cdk_pkt_write (so, pkt); + cdk_stream_close (so); + return rc; +} diff --git a/src/daemon/https/openpgp/Makefile.am b/src/daemon/https/openpgp/Makefile.am new file mode 100644 index 00000000..94c27ac1 --- /dev/null +++ b/src/daemon/https/openpgp/Makefile.am @@ -0,0 +1,27 @@ +SUBDIRS = . + +AM_CPPFLAGS = -I./includes \ +-I$(top_srcdir)/src/daemon/https/includes \ +-I$(top_srcdir)/src/daemon/https/lgl \ +-I$(top_srcdir)/src/daemon/https/x509 \ +-I$(top_srcdir)/src/daemon/https/tls \ +-I$(top_srcdir)/src/daemon/https/openpgp \ +-I$(top_srcdir)/src/daemon/https/opencdk \ +-I$(top_srcdir)/src/daemon/https/minitasn1 + +noinst_LTLIBRARIES = libopenpgp.la + +# libopenpgp_la_LDFLAGS = -l$(top_srcdir)/src/daemon/https/libextra/.lib/libextra.la + +libopenpgp_la_SOURCES = \ +pgp_privkey.c \ +compat.c \ +pgp_verify.c \ +extras.c \ +pgp.c + +# x libextra source list +libopenpgp_la_SOURCES += \ +gnutls_openpgp.c \ +gnutls_ia.c \ +gnutls_extra.c diff --git a/src/daemon/https/openpgp/compat.c b/src/daemon/https/openpgp/compat.c new file mode 100644 index 00000000..1ba7177a --- /dev/null +++ b/src/daemon/https/openpgp/compat.c @@ -0,0 +1,252 @@ +/* + * Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation + * + * Author: Timo Schulz, Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS-EXTRA. + * + * GNUTLS-EXTRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GNUTLS-EXTRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* Compatibility functions on OpenPGP key parsing. + */ + +#include +#include +#include +#include "openpgp.h" + +/*- + * gnutls_openpgp_verify_key - Verify all signatures on the key + * @cert_list: the structure that holds the certificates. + * @cert_list_lenght: the items in the cert_list. + * @status: the output of the verification function + * + * Verify all signatures in the certificate list. When the key + * is not available, the signature is skipped. + * + * The return value is one of the CertificateStatus entries. + * + * NOTE: this function does not verify using any "web of trust". You + * may use GnuPG for that purpose, or any other external PGP application. + -*/ +int +_gnutls_openpgp_verify_key (const gnutls_certificate_credentials_t cred, + const gnutls_datum_t * cert_list, + int cert_list_length, unsigned int *status) +{ + int ret = 0; + gnutls_openpgp_crt_t key = NULL; + unsigned int verify = 0, verify_self = 0; + + if (!cert_list || cert_list_length != 1) + { + gnutls_assert (); + return GNUTLS_E_NO_CERTIFICATE_FOUND; + } + + ret = gnutls_openpgp_crt_init (&key); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + ret = + gnutls_openpgp_crt_import (key, &cert_list[0], GNUTLS_OPENPGP_FMT_RAW); + if (ret < 0) + { + gnutls_assert (); + goto leave; + } + +#ifndef KEYRING_HACK + if (cred->keyring != NULL) + { + ret = gnutls_openpgp_crt_verify_ring (key, cred->keyring, 0, &verify); + if (ret < 0) + { + gnutls_assert (); + goto leave; + } + } +#else + { + gnutls_openpgp_keyring_t kring; + + ret = gnutls_openpgp_keyring_init (&kring); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + ret = + gnutls_openpgp_keyring_import (kring, &cred->keyring, + cred->keyring_format); + if (ret < 0) + { + gnutls_assert (); + gnutls_openpgp_keyring_deinit (kring); + return ret; + } + + ret = gnutls_openpgp_crt_verify_ring (key, kring, 0, &verify); + if (ret < 0) + { + gnutls_assert (); + gnutls_openpgp_keyring_deinit (kring); + return ret; + } + gnutls_openpgp_keyring_deinit (kring); + } +#endif + + /* Now try the self signature. */ + ret = gnutls_openpgp_crt_verify_self (key, 0, &verify_self); + if (ret < 0) + { + gnutls_assert (); + goto leave; + } + + *status = verify_self | verify; + +#ifndef KEYRING_HACK + /* If we only checked the self signature. */ + if (!cred->keyring) +#else + if (!cred->keyring.data || !cred->keyring.size) +#endif + *status |= GNUTLS_CERT_SIGNER_NOT_FOUND; + + + ret = 0; + +leave: + gnutls_openpgp_crt_deinit (key); + + return ret; +} + +/*- + * gnutls_openpgp_fingerprint - Gets the fingerprint + * @cert: the raw data that contains the OpenPGP public key. + * @fpr: the buffer to save the fingerprint. + * @fprlen: the integer to save the length of the fingerprint. + * + * Returns the fingerprint of the OpenPGP key. Depence on the algorithm, + * the fingerprint can be 16 or 20 bytes. + -*/ +int +_gnutls_openpgp_fingerprint (const gnutls_datum_t * cert, + unsigned char *fpr, size_t * fprlen) +{ + gnutls_openpgp_crt_t key; + int ret; + + ret = gnutls_openpgp_crt_init (&key); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + ret = gnutls_openpgp_crt_import (key, cert, GNUTLS_OPENPGP_FMT_RAW); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + ret = gnutls_openpgp_crt_get_fingerprint (key, fpr, fprlen); + gnutls_openpgp_crt_deinit (key); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + return 0; +} + +/*- + * gnutls_openpgp_get_raw_key_creation_time - Extract the timestamp + * @cert: the raw data that contains the OpenPGP public key. + * + * Returns the timestamp when the OpenPGP key was created. + -*/ +time_t +_gnutls_openpgp_get_raw_key_creation_time (const gnutls_datum_t * cert) +{ + gnutls_openpgp_crt_t key; + int ret; + time_t tim; + + ret = gnutls_openpgp_crt_init (&key); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + ret = gnutls_openpgp_crt_import (key, cert, GNUTLS_OPENPGP_FMT_RAW); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + tim = gnutls_openpgp_crt_get_creation_time (key); + + gnutls_openpgp_crt_deinit (key); + + return tim; +} + + +/*- + * gnutls_openpgp_get_raw_key_expiration_time - Extract the expire date + * @cert: the raw data that contains the OpenPGP public key. + * + * Returns the time when the OpenPGP key expires. A value of '0' means + * that the key doesn't expire at all. + -*/ +time_t +_gnutls_openpgp_get_raw_key_expiration_time (const gnutls_datum_t * cert) +{ + gnutls_openpgp_crt_t key; + int ret; + time_t tim; + + ret = gnutls_openpgp_crt_init (&key); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + ret = gnutls_openpgp_crt_import (key, cert, GNUTLS_OPENPGP_FMT_RAW); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + tim = gnutls_openpgp_crt_get_expiration_time (key); + + gnutls_openpgp_crt_deinit (key); + + return tim; +} diff --git a/src/daemon/https/openpgp/extras.c b/src/daemon/https/openpgp/extras.c new file mode 100644 index 00000000..d3c755d4 --- /dev/null +++ b/src/daemon/https/openpgp/extras.c @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2007 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos, Timo Schulz + * + * This file is part of GNUTLS-EXTRA. + * + * GNUTLS-EXTRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GNUTLS-EXTRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* Functions on OpenPGP keyring parsing + */ + +#include +#include +#include +#include +#include +#include +#include "openpgp.h" + +/* Keyring stuff. */ + +/** + * gnutls_openpgp_keyring_init - This function initializes a gnutls_openpgp_keyring_t structure + * @keyring: The structure to be initialized + *- + * This function will initialize an OpenPGP keyring structure. + * + * Returns 0 on success. + * + **/ +int gnutls_openpgp_keyring_init(gnutls_openpgp_keyring_t * keyring) +{ + *keyring = gnutls_calloc(1, sizeof(gnutls_openpgp_keyring_int)); + + if (*keyring) + return 0; /* success */ + return GNUTLS_E_MEMORY_ERROR; +} + +/** + * gnutls_openpgp_keyring_deinit - This function deinitializes memory used by a gnutls_openpgp_keyring_t structure + * @keyring: The structure to be initialized + * + * This function will deinitialize a keyring structure. + * + **/ +void gnutls_openpgp_keyring_deinit(gnutls_openpgp_keyring_t keyring) +{ + if (!keyring) + return; + + if (keyring->db) + { + cdk_keydb_free(keyring->db); + keyring->db = NULL; + } + + /* In some cases the stream is also stored outside the keydb context + and we need to close it here then. */ + if (keyring->db_stream) + { + cdk_stream_close(keyring->db_stream); + keyring->db_stream = NULL; + } + + gnutls_free(keyring); +} + +/** + * gnutls_openpgp_keyring_check_id - Check if a key id exists in the keyring + * @ring: holds the keyring to check against + * @keyid: will hold the keyid to check for. + * @flags: unused (should be 0) + * + * Check if a given key ID exists in the keyring. + * + * Returns 0 on success (if keyid exists) and a negative error code + * on failure. + **/ +int gnutls_openpgp_keyring_check_id(gnutls_openpgp_keyring_t ring, + const unsigned char keyid[8], + unsigned int flags) +{ + cdk_pkt_pubkey_t pk; + uint32_t id[2]; + + id[0] = _gnutls_read_uint32(keyid); + id[1] = _gnutls_read_uint32(&keyid[4]); + + if (!cdk_keydb_get_pk(ring->db, id, &pk)) + { + cdk_pk_release(pk); + return 0; + } + + _gnutls_debug_log ("PGP: key not found %08lX\n", (unsigned long) id[1]); + return GNUTLS_E_NO_CERTIFICATE_FOUND; +} + +/** + * gnutls_openpgp_keyring_import - Import a raw- or Base64-encoded OpenPGP keyring + * @keyring: The structure to store the parsed key. + * @data: The RAW or BASE64 encoded keyring. + * @format: One of #gnutls_openpgp_keyring_fmt elements. + * + * This function will convert the given RAW or Base64 encoded keyring to the + * native #gnutls_openpgp_keyring_t format. The output will be stored in + * 'keyring'. + * + * Returns 0 on success. + * + **/ +int gnutls_openpgp_keyring_import(gnutls_openpgp_keyring_t keyring, + const gnutls_datum_t * data, + gnutls_openpgp_crt_fmt_t format) +{ + cdk_error_t err; + cdk_stream_t input; + + _gnutls_debug_log ("PGP: keyring import format '%s'\n", + format == GNUTLS_OPENPGP_FMT_RAW ? "raw" : "base64"); + + if (format == GNUTLS_OPENPGP_FMT_RAW) + { + err + = cdk_keydb_new(&keyring->db, CDK_DBTYPE_DATA, data->data, data->size); + if (err) + gnutls_assert (); + return _gnutls_map_cdk_rc (err); + } + + /* Create a new stream from the given data, which means to + allocate a new stream and to write the data in the stream. + Then push the armor filter to decode the data and to store + it in the raw format. */ + err = cdk_stream_tmp_from_mem(data->data, data->size, &input); + if (!err) + err = cdk_stream_set_armor_flag(input, 0); + if (!err) + err = cdk_keydb_new_from_stream(&keyring->db, 0, input); + if (err) + { + cdk_stream_close(input); + gnutls_assert (); + } + else + /* The keydb function will not close the stream itself, so we need to + store it separately to close it later. */ + keyring->db_stream = input; + + return _gnutls_map_cdk_rc (err); +} diff --git a/src/daemon/https/openpgp/gnutls_extra.c b/src/daemon/https/openpgp/gnutls_extra.c new file mode 100644 index 00000000..7c47e3f1 --- /dev/null +++ b/src/daemon/https/openpgp/gnutls_extra.c @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2001, 2004, 2005, 2007 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS-EXTRA. + * + * GNUTLS-EXTRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GNUTLS-EXTRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +/* the number of the compression algorithms available in the compression + * structure. + */ +extern int _gnutls_comp_algorithms_size; + +/* Functions in gnutls that have not been initialized. + */ + +static int _gnutls_init_extra = 0; + +/** + * gnutls_global_init_extra - This function initializes the global state of gnutls-extra + * + * This function initializes the global state of gnutls-extra library + * to defaults. Returns zero on success. + * + * Note that gnutls_global_init() has to be called before this + * function. If this function is not called then the gnutls-extra + * library will not be usable. + * + **/ +int +gnutls_global_init_extra (void) +{ + int ret; + + /* If the version of libgnutls != version of + * libextra, then do not initialize the library. + * This is because it may break things. + */ + if (strcmp (gnutls_check_version (NULL), VERSION) != 0) + { + return GNUTLS_E_LIBRARY_VERSION_MISMATCH; + } + + _gnutls_init_extra++; + + if (_gnutls_init_extra != 1) + { + return 0; + } + + /* Register the openpgp functions. This is because some + * of them are defined to be NULL in the main library. + */ + _gnutls_add_openpgp_functions (_gnutls_openpgp_verify_key, + _gnutls_openpgp_get_raw_key_creation_time, + _gnutls_openpgp_get_raw_key_expiration_time, + _gnutls_openpgp_fingerprint, + _gnutls_openpgp_request_key, + _gnutls_openpgp_raw_key_to_gcert, + _gnutls_openpgp_raw_privkey_to_gkey, + _gnutls_openpgp_crt_to_gcert, + _gnutls_openpgp_privkey_to_gkey, + gnutls_openpgp_crt_deinit, + gnutls_openpgp_keyring_deinit, + gnutls_openpgp_privkey_deinit); + + return 0; +} + +#include + +/** + * gnutls_extra_check_version - This function checks the library's version + * @req_version: the version to check + * + * Check that the version of the gnutls-extra library is at minimum + * the requested one and return the version string; return NULL if the + * condition is not satisfied. If a NULL is passed to this function, + * no check is done, but the version string is simply returned. + * + **/ +const char * +gnutls_extra_check_version (const char *req_version) +{ + if (!req_version || strverscmp (req_version, VERSION) <= 0) + return VERSION; + + return NULL; +} diff --git a/src/daemon/https/openpgp/gnutls_extra.h b/src/daemon/https/openpgp/gnutls_extra.h new file mode 100644 index 00000000..de7fc889 --- /dev/null +++ b/src/daemon/https/openpgp/gnutls_extra.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2002, 2003, 2004, 2005, 2007 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS-EXTRA. + * + * GNUTLS-EXTRA is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * GNUTLS-EXTRA 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 GNUTLS-EXTRA; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +#include + +typedef int (*OPENPGP_VERIFY_KEY_FUNC) (const + gnutls_certificate_credentials_t, + const gnutls_datum_t *, int, + unsigned int *); + +typedef time_t (*OPENPGP_KEY_CREATION_TIME_FUNC) (const gnutls_datum_t *); +typedef time_t (*OPENPGP_KEY_EXPIRATION_TIME_FUNC) (const gnutls_datum_t *); +typedef int (*OPENPGP_KEY_REQUEST) (gnutls_session_t, gnutls_datum_t *, + const gnutls_certificate_credentials_t, + opaque *, int); + +typedef int (*OPENPGP_FINGERPRINT) (const gnutls_datum_t *, + unsigned char *, size_t *); + +typedef int (*OPENPGP_RAW_KEY_TO_GCERT) (gnutls_cert *, + const gnutls_datum_t *); +typedef int (*OPENPGP_RAW_PRIVKEY_TO_GKEY) (gnutls_privkey *, + const gnutls_datum_t *); + +typedef int (*OPENPGP_KEY_TO_GCERT) (gnutls_cert *, gnutls_openpgp_crt_t); +typedef int (*OPENPGP_PRIVKEY_TO_GKEY) (gnutls_privkey *, + gnutls_openpgp_privkey_t); +typedef void (*OPENPGP_KEY_DEINIT) (gnutls_openpgp_crt_t); +typedef void (*OPENPGP_PRIVKEY_DEINIT) (gnutls_openpgp_privkey_t); diff --git a/src/daemon/https/openpgp/gnutls_ia.c b/src/daemon/https/openpgp/gnutls_ia.c new file mode 100644 index 00000000..55b22e9c --- /dev/null +++ b/src/daemon/https/openpgp/gnutls_ia.c @@ -0,0 +1,905 @@ +/* + * Copyright (C) 2005, 2006 Free Software Foundation + * + * Author: Simon Josefsson + * + * This file is part of GNUTLS-EXTRA. + * + * GNUTLS-EXTRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GNUTLS-EXTRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "gnutls_int.h" +#include "gnutls_record.h" +#include "gnutls_errors.h" +#include "gnutls_num.h" +#include "gnutls_state.h" + +#define CHECKSUM_SIZE 12 + +struct gnutls_ia_client_credentials_st +{ + gnutls_ia_avp_func avp_func; + void *avp_ptr; +}; + +struct gnutls_ia_server_credentials_st +{ + gnutls_ia_avp_func avp_func; + void *avp_ptr; +}; + +static const char server_finished_label[] = "server phase finished"; +static const char client_finished_label[] = "client phase finished"; +static const char inner_permutation_label[] = "inner secret permutation"; +static const char challenge_label[] = "inner application challenge"; + +/* + * The TLS/IA packet is the InnerApplication token, described as + * follows in draft-funk-tls-inner-application-extension-01.txt: + * + * enum { + * application_payload(0), intermediate_phase_finished(1), + * final_phase_finished(2), (255) + * } InnerApplicationType; + * + * struct { + * InnerApplicationType msg_type; + * uint24 length; + * select (InnerApplicationType) { + * case application_payload: ApplicationPayload; + * case intermediate_phase_finished: IntermediatePhaseFinished; + * case final_phase_finished: FinalPhaseFinished; + * } body; + * } InnerApplication; + * + */ + +/* Send TLS/IA data. If data==NULL && sizeofdata==NULL, then the last + send was interrupted for some reason, and then we try to send it + again. Returns the number of bytes sent, or an error code. If + this return E_AGAIN and E_INTERRUPTED, call this function again + with data==NULL&&sizeofdata=0NULL until it returns successfully. */ +static ssize_t +_gnutls_send_inner_application (gnutls_session_t session, + gnutls_ia_apptype_t msg_type, + const char *data, size_t sizeofdata) +{ + opaque *p = NULL; + size_t plen = 0; + ssize_t len; + + if (data != NULL) + { + plen = sizeofdata + 4; + p = gnutls_malloc (plen); + if (!p) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + *(unsigned char *) p = (unsigned char) (msg_type & 0xFF); + _gnutls_write_uint24 (sizeofdata, p + 1); + memcpy (p + 4, data, sizeofdata); + } + + len = _gnutls_send_int (session, GNUTLS_INNER_APPLICATION, -1, p, plen); + + if (p) + gnutls_free (p); + + return len; +} + +/* Receive TLS/IA data. Store received TLS/IA message type in + *MSG_TYPE, and the data in DATA of max SIZEOFDATA size. Return the + number of bytes read, or an error code. */ +static ssize_t +_gnutls_recv_inner_application (gnutls_session_t session, + gnutls_ia_apptype_t * msg_type, + opaque * data, size_t sizeofdata) +{ + ssize_t len; + opaque pkt[4]; + + len = _gnutls_recv_int (session, GNUTLS_INNER_APPLICATION, -1, pkt, 4); + if (len != 4) + { + gnutls_assert (); + return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; + } + + *msg_type = pkt[0]; + len = _gnutls_read_uint24 (&pkt[1]); + + if (*msg_type != GNUTLS_IA_APPLICATION_PAYLOAD && len != CHECKSUM_SIZE) + { + gnutls_assert (); + return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; + } + + if (sizeofdata < len) + { + /* XXX push back pkt to IA buffer? */ + gnutls_assert (); + return GNUTLS_E_SHORT_MEMORY_BUFFER; + } + + if (len > 0) + { + int tmplen = len; + + len = _gnutls_recv_int (session, GNUTLS_INNER_APPLICATION, -1, + data, tmplen); + if (len != tmplen) + { + gnutls_assert (); + /* XXX Correct? */ + return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; + } + } + + return len; +} + +/* Apply the TLS PRF using the TLS/IA inner secret as keying material, + where the seed is the client random concatenated with the server + random concatenated EXTRA of EXTRA_SIZE length (which can be NULL/0 + respectively). LABEL and LABEL_SIZE is used as the label. The + result is placed in pre-allocated OUT of OUTSIZE length. */ +static int +_gnutls_ia_prf (gnutls_session_t session, + size_t label_size, + const char *label, + size_t extra_size, + const char *extra, size_t outsize, opaque * out) +{ + int ret; + opaque *seed; + size_t seedsize = 2 * TLS_RANDOM_SIZE + extra_size; + + seed = gnutls_malloc (seedsize); + if (!seed) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + memcpy (seed, session->security_parameters.server_random, TLS_RANDOM_SIZE); + memcpy (seed + TLS_RANDOM_SIZE, session->security_parameters.client_random, + TLS_RANDOM_SIZE); + memcpy (seed + 2 * TLS_RANDOM_SIZE, extra, extra_size); + + ret = _gnutls_PRF (session, session->security_parameters.inner_secret, + TLS_MASTER_SIZE, + label, label_size, seed, seedsize, outsize, out); + + gnutls_free (seed); + + return ret; +} + +/** + * gnutls_ia_permute_inner_secret: + * @session: is a #gnutls_session_t structure. + * @session_keys_size: Size of generated session keys (0 if none). + * @session_keys: Generated session keys, used to permute inner secret + * (NULL if none). + * + * Permute the inner secret using the generated session keys. + * + * This can be called in the TLS/IA AVP callback to mix any generated + * session keys with the TLS/IA inner secret. + * + * Return value: Return zero on success, or a negative error code. + **/ +int +gnutls_ia_permute_inner_secret (gnutls_session_t session, + size_t session_keys_size, + const char *session_keys) +{ + return _gnutls_ia_prf (session, + sizeof (inner_permutation_label) - 1, + inner_permutation_label, + session_keys_size, + session_keys, + TLS_RANDOM_SIZE, + session->security_parameters.inner_secret); +} + +/** + * gnutls_ia_generate_challenge: + * @session: is a #gnutls_session_t structure. + * @buffer_size: size of output buffer. + * @buffer: pre-allocated buffer to contain @buffer_size bytes of output. + * + * Generate an application challenge that the client cannot control or + * predict, based on the TLS/IA inner secret. + * + * Return value: Returns 0 on success, or an negative error code. + **/ +int +gnutls_ia_generate_challenge (gnutls_session_t session, + size_t buffer_size, char *buffer) +{ + return _gnutls_ia_prf (session, + sizeof (challenge_label) - 1, + challenge_label, 0, NULL, buffer_size, buffer); +} + +/** + * gnutls_ia_extract_inner_secret: + * @session: is a #gnutls_session_t structure. + * @buffer: pre-allocated buffer to hold 48 bytes of inner secret. + * + * Copy the 48 bytes large inner secret into the specified buffer + * + * This function is typically used after the TLS/IA handshake has + * concluded. The TLS/IA inner secret can be used as input to a PRF + * to derive session keys. Do not use the inner secret directly as a + * session key, because for a resumed session that does not include an + * application phase, the inner secret will be identical to the inner + * secret in the original session. It is important to include, for + * example, the client and server randomness when deriving a sesssion + * key from the inner secret. + **/ +void +gnutls_ia_extract_inner_secret (gnutls_session_t session, char *buffer) +{ + memcpy (buffer, session->security_parameters.inner_secret, TLS_MASTER_SIZE); +} + +/** + * gnutls_ia_endphase_send: + * @session: is a #gnutls_session_t structure. + * @final_p: Set iff this should signal the final phase. + * + * Send a TLS/IA end phase message. + * + * In the client, this should only be used to acknowledge an end phase + * message sent by the server. + * + * In the server, this can be called instead of gnutls_ia_send() if + * the server wishes to end an application phase. + * + * Return value: Return 0 on success, or an error code. + **/ +int +gnutls_ia_endphase_send (gnutls_session_t session, int final_p) +{ + opaque local_checksum[CHECKSUM_SIZE]; + int client = session->security_parameters.entity == GNUTLS_CLIENT; + const char *label = client ? client_finished_label : server_finished_label; + int size_of_label = client ? sizeof (client_finished_label) : + sizeof (server_finished_label); + ssize_t len; + int ret; + + ret = _gnutls_PRF (session, session->security_parameters.inner_secret, + TLS_MASTER_SIZE, label, size_of_label - 1, + /* XXX specification unclear on seed. */ + "", 0, CHECKSUM_SIZE, local_checksum); + if (ret < 0) + return ret; + + len = _gnutls_send_inner_application + (session, + final_p ? GNUTLS_IA_FINAL_PHASE_FINISHED : + GNUTLS_IA_INTERMEDIATE_PHASE_FINISHED, local_checksum, CHECKSUM_SIZE); + + /* XXX Instead of calling this function over and over...? + * while (len == GNUTLS_E_AGAIN || len == GNUTLS_E_INTERRUPTED) + * len = _gnutls_io_write_flush(session); + */ + + if (len < 0) + { + gnutls_assert (); + return len; + } + + return 0; +} + +/** + * gnutls_ia_verify_endphase: + * @session: is a #gnutls_session_t structure. + * @checksum: 12-byte checksum data, received from gnutls_ia_recv(). + * + * Verify TLS/IA end phase checksum data. If verification fails, the + * %GNUTLS_A_INNER_APPLICATION_VERIFICATION alert is sent to the other + * sie. + * + * This function is called when gnutls_ia_recv() return + * %GNUTLS_E_WARNING_IA_IPHF_RECEIVED or + * %GNUTLS_E_WARNING_IA_FPHF_RECEIVED. + * + * Return value: Return 0 on successful verification, or an error + * code. If the checksum verification of the end phase message fails, + * %GNUTLS_E_IA_VERIFY_FAILED is returned. + **/ +int +gnutls_ia_verify_endphase (gnutls_session_t session, const char *checksum) +{ + char local_checksum[CHECKSUM_SIZE]; + int client = session->security_parameters.entity == GNUTLS_CLIENT; + const char *label = client ? server_finished_label : client_finished_label; + int size_of_label = client ? sizeof (server_finished_label) : + sizeof (client_finished_label); + int ret; + + ret = _gnutls_PRF (session, session->security_parameters.inner_secret, + TLS_MASTER_SIZE, + label, size_of_label - 1, + "", 0, CHECKSUM_SIZE, local_checksum); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + if (memcmp (local_checksum, checksum, CHECKSUM_SIZE) != 0) + { + ret = gnutls_alert_send (session, GNUTLS_AL_FATAL, + GNUTLS_A_INNER_APPLICATION_VERIFICATION); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + return GNUTLS_E_IA_VERIFY_FAILED; + } + + return 0; +} + +/** + * gnutls_ia_send: Send peer the specified TLS/IA data. + * @session: is a #gnutls_session_t structure. + * @data: contains the data to send + * @sizeofdata: is the length of the data + * + * Send TLS/IA application payload data. This function has the + * similar semantics with send(). The only difference is that is + * accepts a GNUTLS session, and uses different error codes. + * + * The TLS/IA protocol is synchronous, so you cannot send more than + * one packet at a time. The client always send the first packet. + * + * To finish an application phase in the server, use + * gnutls_ia_endphase_send(). The client cannot end an application + * phase unilaterally; rather, a client is required to respond with an + * endphase of its own if gnutls_ia_recv indicates that the server has + * sent one. + * + * If the EINTR is returned by the internal push function (the default + * is send()} then %GNUTLS_E_INTERRUPTED will be returned. If + * %GNUTLS_E_INTERRUPTED or %GNUTLS_E_AGAIN is returned, you must call + * this function again, with the same parameters; alternatively you + * could provide a %NULL pointer for data, and 0 for size. + * + * Returns the number of bytes sent, or a negative error code. + **/ +ssize_t +gnutls_ia_send (gnutls_session_t session, const char *data, size_t sizeofdata) +{ + ssize_t len; + + len = _gnutls_send_inner_application (session, + GNUTLS_IA_APPLICATION_PAYLOAD, + data, sizeofdata); + + return len; +} + +/** + * gnutls_ia_recv - read data from the TLS/IA protocol + * @session: is a #gnutls_session_t structure. + * @data: the buffer that the data will be read into, must hold >= 12 bytes. + * @sizeofdata: the number of requested bytes, must be >= 12. + * + * Receive TLS/IA data. This function has the similar semantics with + * recv(). The only difference is that is accepts a GNUTLS session, + * and uses different error codes. + * + * If the server attempt to finish an application phase, this function + * will return %GNUTLS_E_WARNING_IA_IPHF_RECEIVED or + * %GNUTLS_E_WARNING_IA_FPHF_RECEIVED. The caller should then invoke + * gnutls_ia_verify_endphase(), and if it runs the client side, also + * send an endphase message of its own using gnutls_ia_endphase_send. + * + * If EINTR is returned by the internal push function (the default is + * @code{recv()}) then GNUTLS_E_INTERRUPTED will be returned. If + * GNUTLS_E_INTERRUPTED or GNUTLS_E_AGAIN is returned, you must call + * this function again, with the same parameters; alternatively you + * could provide a NULL pointer for data, and 0 for size. + * + * Returns the number of bytes received. A negative error code is + * returned in case of an error. The + * %GNUTLS_E_WARNING_IA_IPHF_RECEIVED and + * %GNUTLS_E_WARNING_IA_FPHF_RECEIVED errors are returned when an + * application phase finished message has been sent by the server. + **/ +ssize_t +gnutls_ia_recv (gnutls_session_t session, char *data, size_t sizeofdata) +{ + gnutls_ia_apptype_t msg_type; + ssize_t len; + + len = _gnutls_recv_inner_application (session, &msg_type, data, sizeofdata); + + if (msg_type == GNUTLS_IA_INTERMEDIATE_PHASE_FINISHED) + return GNUTLS_E_WARNING_IA_IPHF_RECEIVED; + else if (msg_type == GNUTLS_IA_FINAL_PHASE_FINISHED) + return GNUTLS_E_WARNING_IA_FPHF_RECEIVED; + + return len; +} + +/* XXX rewrite the following two functions as state machines, to + handle EAGAIN/EINTERRUPTED? just add more problems to callers, + though. */ + +int +_gnutls_ia_client_handshake (gnutls_session_t session) +{ + char *buf = NULL; + size_t buflen = 0; + char tmp[1024]; /* XXX */ + ssize_t len; + int ret; + const struct gnutls_ia_client_credentials_st *cred = + _gnutls_get_cred (session->key, GNUTLS_CRD_IA, NULL); + + if (cred == NULL) + return GNUTLS_E_INTERNAL_ERROR; + + while (1) + { + char *avp; + size_t avplen; + + ret = cred->avp_func (session, cred->avp_ptr, + buf, buflen, &avp, &avplen); + if (ret) + { + int tmpret; + tmpret = gnutls_alert_send (session, GNUTLS_AL_FATAL, + GNUTLS_A_INNER_APPLICATION_FAILURE); + if (tmpret < 0) + gnutls_assert (); + return ret; + } + + len = gnutls_ia_send (session, avp, avplen); + gnutls_free (avp); + if (len < 0) + return len; + + len = gnutls_ia_recv (session, tmp, sizeof (tmp)); + if (len == GNUTLS_E_WARNING_IA_IPHF_RECEIVED || + len == GNUTLS_E_WARNING_IA_FPHF_RECEIVED) + { + ret = gnutls_ia_verify_endphase (session, tmp); + if (ret < 0) + return ret; + + ret = gnutls_ia_endphase_send + (session, len == GNUTLS_E_WARNING_IA_FPHF_RECEIVED); + if (ret < 0) + return ret; + } + + if (len == GNUTLS_E_WARNING_IA_IPHF_RECEIVED) + { + buf = NULL; + buflen = 0; + continue; + } + else if (len == GNUTLS_E_WARNING_IA_FPHF_RECEIVED) + break; + + if (len < 0) + return len; + + buflen = len; + buf = tmp; + } + + return 0; +} + +int +_gnutls_ia_server_handshake (gnutls_session_t session) +{ + gnutls_ia_apptype_t msg_type; + ssize_t len; + char buf[1024]; + int ret; + const struct gnutls_ia_server_credentials_st *cred = + _gnutls_get_cred (session->key, GNUTLS_CRD_IA, NULL); + + if (cred == NULL) + return GNUTLS_E_INTERNAL_ERROR; + + do + { + char *avp; + size_t avplen; + + len = gnutls_ia_recv (session, buf, sizeof (buf)); + if (len == GNUTLS_E_WARNING_IA_IPHF_RECEIVED || + len == GNUTLS_E_WARNING_IA_FPHF_RECEIVED) + { + ret = gnutls_ia_verify_endphase (session, buf); + if (ret < 0) + return ret; + } + + if (len == GNUTLS_E_WARNING_IA_IPHF_RECEIVED) + continue; + else if (len == GNUTLS_E_WARNING_IA_FPHF_RECEIVED) + break; + + if (len < 0) + return len; + + avp = NULL; + avplen = 0; + + ret = cred->avp_func (session, cred->avp_ptr, buf, len, &avp, &avplen); + if (ret < 0) + { + int tmpret; + tmpret = gnutls_alert_send (session, GNUTLS_AL_FATAL, + GNUTLS_A_INNER_APPLICATION_FAILURE); + if (tmpret < 0) + gnutls_assert (); + return ret; + } + + msg_type = ret; + + if (msg_type != GNUTLS_IA_APPLICATION_PAYLOAD) + { + ret = gnutls_ia_endphase_send (session, msg_type == + GNUTLS_IA_FINAL_PHASE_FINISHED); + if (ret < 0) + return ret; + } + else + { + len = gnutls_ia_send (session, avp, avplen); + gnutls_free (avp); + if (len < 0) + return len; + } + } + while (1); + + return 0; +} + +/** + * gnutls_ia_handshake_p: + * @session: is a #gnutls_session_t structure. + * + * Predicate to be used after gnutls_handshake() to decide whether to + * invoke gnutls_ia_handshake(). Usable by both clients and servers. + * + * Return value: non-zero if TLS/IA handshake is expected, zero + * otherwise. + **/ +int +gnutls_ia_handshake_p (gnutls_session_t session) +{ + tls_ext_st *ext = &session->security_parameters.extensions; + + /* Either local side or peer doesn't do TLS/IA: don't do IA */ + + if (!ext->gnutls_ia_enable || !ext->gnutls_ia_peer_enable) + return 0; + + /* Not resuming or we don't allow skipping on resumption locally: do IA */ + + if (!ext->gnutls_ia_allowskip || !gnutls_session_is_resumed (session)) + return 1; + + /* If we're resuming and we and the peer both allow skipping on resumption: + * don't do IA */ + + return !ext->gnutls_ia_peer_allowskip; +} + + +/** + * gnutls_ia_handshake: + * @session: is a #gnutls_session_t structure. + * + * Perform a TLS/IA handshake. This should be called after + * gnutls_handshake() iff gnutls_ia_handshake_p(). + * + * Return 0 on success, or an error code. + **/ +int +gnutls_ia_handshake (gnutls_session_t session) +{ + int ret; + + if (session->security_parameters.entity == GNUTLS_CLIENT) + ret = _gnutls_ia_client_handshake (session); + else + ret = _gnutls_ia_server_handshake (session); + + return ret; +} + +/** + * gnutls_ia_allocate_client_credentials - Used to allocate an gnutls_ia_server_credentials_t structure + * @sc: is a pointer to an #gnutls_ia_server_credentials_t structure. + * + * This structure is complex enough to manipulate directly thus this + * helper function is provided in order to allocate it. + * + * Adding this credential to a session will enable TLS/IA, and will + * require an Application Phase after the TLS handshake (if the server + * support TLS/IA). Use gnutls_ia_require_inner_phase() to toggle the + * TLS/IA mode. + * + * Returns 0 on success. + **/ +int +gnutls_ia_allocate_client_credentials (gnutls_ia_client_credentials_t * sc) +{ + *sc = gnutls_calloc (1, sizeof (**sc)); + + if (*sc == NULL) + return GNUTLS_E_MEMORY_ERROR; + + return 0; +} + +/** + * gnutls_ia_free_client_credentials - Used to free an allocated #gnutls_ia_client_credentials_t structure + * @sc: is an #gnutls_ia_client_credentials_t structure. + * + * This structure is complex enough to manipulate directly thus this + * helper function is provided in order to free (deallocate) it. + * + **/ +void +gnutls_ia_free_client_credentials (gnutls_ia_client_credentials_t sc) +{ + gnutls_free (sc); +} + +/** + * gnutls_ia_set_client_avp_function - Used to set a AVP callback + * @cred: is a #gnutls_ia_client_credentials_t structure. + * @avp_func: is the callback function + * + * Set the TLS/IA AVP callback handler used for the session. + * + * The AVP callback is called to process AVPs received from the + * server, and to get a new AVP to send to the server. + * + * The callback's function form is: + * int (*avp_func) (gnutls_session_t session, void *ptr, + * const char *last, size_t lastlen, + * char **next, size_t *nextlen); + * + * The @session parameter is the #gnutls_session_t structure + * corresponding to the current session. The @ptr parameter is the + * application hook pointer, set through + * gnutls_ia_set_client_avp_ptr(). The AVP received from the server + * is present in @last of @lastlen size, which will be %NULL on the + * first invocation. The newly allocated output AVP to send to the + * server should be placed in *@next of *@nextlen size. + * + * The callback may invoke gnutls_ia_permute_inner_secret() to mix any + * generated session keys with the TLS/IA inner secret. + * + * Return 0 (%GNUTLS_IA_APPLICATION_PAYLOAD) on success, or a negative + * error code to abort the TLS/IA handshake. + * + * Note that the callback must use allocate the @next parameter using + * gnutls_malloc(), because it is released via gnutls_free() by the + * TLS/IA handshake function. + * + **/ +void +gnutls_ia_set_client_avp_function (gnutls_ia_client_credentials_t cred, + gnutls_ia_avp_func avp_func) +{ + cred->avp_func = avp_func; +} + +/** + * gnutls_ia_set_client_avp_ptr - Sets a pointer to be sent to TLS/IA callback + * @cred: is a #gnutls_ia_client_credentials_t structure. + * @ptr: is the pointer + * + * Sets the pointer that will be provided to the TLS/IA callback + * function as the first argument. + * + **/ +void +gnutls_ia_set_client_avp_ptr (gnutls_ia_client_credentials_t cred, void *ptr) +{ + cred->avp_ptr = ptr; +} + +/** + * gnutls_ia_get_client_avp_ptr - Returns the pointer which is sent to TLS/IA callback + * @cred: is a #gnutls_ia_client_credentials_t structure. + * + * Returns the pointer that will be provided to the TLS/IA callback + * function as the first argument. + * + **/ +void * +gnutls_ia_get_client_avp_ptr (gnutls_ia_client_credentials_t cred) +{ + return cred->avp_ptr; +} + +/** + * gnutls_ia_allocate_server_credentials - Used to allocate an gnutls_ia_server_credentials_t structure + * @sc: is a pointer to an #gnutls_ia_server_credentials_t structure. + * + * This structure is complex enough to manipulate directly thus this + * helper function is provided in order to allocate it. + * + * Adding this credential to a session will enable TLS/IA, and will + * require an Application Phase after the TLS handshake (if the client + * support TLS/IA). Use gnutls_ia_require_inner_phase() to toggle the + * TLS/IA mode. + * + * Returns 0 on success. + **/ +int +gnutls_ia_allocate_server_credentials (gnutls_ia_server_credentials_t * sc) +{ + *sc = gnutls_calloc (1, sizeof (**sc)); + + if (*sc == NULL) + return GNUTLS_E_MEMORY_ERROR; + + return 0; +} + +/** + * gnutls_ia_free_server_credentials - Used to free an allocated #gnutls_ia_server_credentials_t structure + * @sc: is an #gnutls_ia_server_credentials_t structure. + * + * This structure is complex enough to manipulate directly thus this + * helper function is provided in order to free (deallocate) it. + * + **/ +void +gnutls_ia_free_server_credentials (gnutls_ia_server_credentials_t sc) +{ + gnutls_free (sc); +} + +/** + * gnutls_ia_set_server_credentials_function - Used to set a AVP callback + * @cred: is a #gnutls_ia_server_credentials_t structure. + * @func: is the callback function + * + * Set the TLS/IA AVP callback handler used for the session. + * + * The callback's function form is: + * int (*avp_func) (gnutls_session_t session, void *ptr, + * const char *last, size_t lastlen, + * char **next, size_t *nextlen); + * + * The @session parameter is the #gnutls_session_t structure + * corresponding to the current session. The @ptr parameter is the + * application hook pointer, set through + * gnutls_ia_set_server_avp_ptr(). The AVP received from the client + * is present in @last of @lastlen size. The newly allocated output + * AVP to send to the client should be placed in *@next of *@nextlen + * size. + * + * The AVP callback is called to process incoming AVPs from the + * client, and to get a new AVP to send to the client. It can also be + * used to instruct the TLS/IA handshake to do go into the + * Intermediate or Final phases. It return a negative error code, or + * an #gnutls_ia_apptype_t message type. + * + * The callback may invoke gnutls_ia_permute_inner_secret() to mix any + * generated session keys with the TLS/IA inner secret. + * + * Specifically, return %GNUTLS_IA_APPLICATION_PAYLOAD (0) to send + * another AVP to the client, return + * %GNUTLS_IA_INTERMEDIATE_PHASE_FINISHED (1) to indicate that an + * IntermediatePhaseFinished message should be sent, and return + * %GNUTLS_IA_FINAL_PHASE_FINISHED (2) to indicate that an + * FinalPhaseFinished message should be sent. In the last two cases, + * the contents of the @next and @nextlen parameter is not used. + * + * Note that the callback must use allocate the @next parameter using + * gnutls_malloc(), because it is released via gnutls_free() by the + * TLS/IA handshake function. + **/ +void +gnutls_ia_set_server_avp_function (gnutls_ia_server_credentials_t cred, + gnutls_ia_avp_func avp_func) +{ + cred->avp_func = avp_func; +} + +/** + * gnutls_ia_set_server_avp_ptr - Sets a pointer to be sent to TLS/IA callback + * @cred: is a #gnutls_ia_client_credentials_t structure. + * @ptr: is the pointer + * + * Sets the pointer that will be provided to the TLS/IA callback + * function as the first argument. + * + **/ +void +gnutls_ia_set_server_avp_ptr (gnutls_ia_server_credentials_t cred, void *ptr) +{ + cred->avp_ptr = ptr; +} + +/** + * gnutls_ia_get_server_avp_ptr - Returns the pointer which is sent to TLS/IA callback + * @cred: is a #gnutls_ia_client_credentials_t structure. + * + * Returns the pointer that will be provided to the TLS/IA callback + * function as the first argument. + * + **/ +void * +gnutls_ia_get_server_avp_ptr (gnutls_ia_server_credentials_t cred) +{ + return cred->avp_ptr; +} + +/** + * gnutls_ia_enable - Indicate willingness for TLS/IA application phases + * @session: is a #gnutls_session_t structure. + * @allow_skip_on_resume: non-zero if local party allows to skip the + * TLS/IA application phases for a resumed session. + * + * Specify whether we must advertise support for the TLS/IA extension + * during the handshake. + * + * At the client side, we always advertise TLS/IA if gnutls_ia_enable + * was called before the handshake; at the server side, we also + * require that the client has advertised that it wants to run TLS/IA + * before including the advertisement, as required by the protocol. + * + * Similarly, at the client side we always advertise that we allow + * TLS/IA to be skipped for resumed sessions if @allow_skip_on_resume + * is non-zero; at the server side, we also require that the session + * is indeed resumable and that the client has also advertised that it + * allows TLS/IA to be skipped for resumed sessions. + * + * After the TLS handshake, call gnutls_ia_handshake_p() to find out + * whether both parties agreed to do a TLS/IA handshake, before + * calling gnutls_ia_handshake() or one of the lower level gnutls_ia_* + * functions. + **/ +void +gnutls_ia_enable (gnutls_session_t session, int allow_skip_on_resume) +{ + session->security_parameters.extensions.gnutls_ia_enable = 1; + session->security_parameters.extensions.gnutls_ia_allowskip = + allow_skip_on_resume; +} diff --git a/src/daemon/https/openpgp/gnutls_openpgp.c b/src/daemon/https/openpgp/gnutls_openpgp.c new file mode 100644 index 00000000..02469463 --- /dev/null +++ b/src/daemon/https/openpgp/gnutls_openpgp.c @@ -0,0 +1,973 @@ +/* + * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation + * + * Author: Timo Schulz + * + * This file is part of GNUTLS-EXTRA. + * + * GNUTLS-EXTRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GNUTLS-EXTRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "gnutls_int.h" +#include "gnutls_errors.h" +#include "gnutls_mpi.h" +#include "gnutls_cert.h" +#include "gnutls_datum.h" +#include "gnutls_global.h" +#include "gnutls_openpgp.h" +#include "read-file.h" +#include +#include +#include +#include +#include +#include + +#define OPENPGP_NAME_SIZE 256 + +#define datum_append(x, y, z) _gnutls_datum_append_m (x, y, z, gnutls_realloc) + + + +static void +release_mpi_array (mpi_t * arr, size_t n) +{ + mpi_t x; + + while (arr && n--) + { + x = *arr; + _gnutls_mpi_release (&x); + *arr = NULL; + arr++; + } +} + + +/* Map an OpenCDK error type to a GnuTLS error type. */ +int +_gnutls_map_cdk_rc (int rc) +{ + switch (rc) + { + case CDK_Success: + return 0; + case CDK_Too_Short: + return GNUTLS_E_SHORT_MEMORY_BUFFER; + case CDK_General_Error: + return GNUTLS_E_INTERNAL_ERROR; + case CDK_File_Error: + return GNUTLS_E_FILE_ERROR; + case CDK_MPI_Error: + return GNUTLS_E_MPI_SCAN_FAILED; + case CDK_Error_No_Key: + return GNUTLS_E_OPENPGP_GETKEY_FAILED; + case CDK_Armor_Error: + return GNUTLS_E_BASE64_DECODING_ERROR; + case CDK_Inv_Value: + return GNUTLS_E_INVALID_REQUEST; + default: + return GNUTLS_E_INTERNAL_ERROR; + } +} + +static unsigned long +buftou32 (const uint8_t * buf) +{ + unsigned a; + a = buf[0] << 24; + a |= buf[1] << 16; + a |= buf[2] << 8; + a |= buf[3]; + return a; +} + +static int +openpgp_pk_to_gnutls_cert (gnutls_cert * cert, cdk_pkt_pubkey_t pk) +{ + uint8_t buf[512 + 2]; + size_t nbytes; + int algo, i; + int rc = 0; + + if (!cert || !pk) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + /* GnuTLS OpenPGP does not support ELG keys */ + if (is_ELG (pk->pubkey_algo)) + { + gnutls_assert (); + return GNUTLS_E_UNWANTED_ALGORITHM; + } + + algo = GNUTLS_PK_RSA; + cert->subject_pk_algorithm = algo; + cert->version = pk->version; + cert->cert_type = GNUTLS_CRT_OPENPGP; + + cert->key_usage = 0; + if (pk->pubkey_usage & CDK_KEY_USG_SIGN) + cert->key_usage = KEY_DIGITAL_SIGNATURE; + if (pk->pubkey_usage & CDK_KEY_USG_ENCR) + cert->key_usage = KEY_KEY_ENCIPHERMENT; + if (!cert->key_usage) /* Fallback code. */ + { + if (pk->pubkey_algo == GCRY_PK_DSA || pk->pubkey_algo == GCRY_PK_RSA_S) + cert->key_usage = KEY_DIGITAL_SIGNATURE; + else if (pk->pubkey_algo == GCRY_PK_RSA_E) + cert->key_usage = KEY_KEY_ENCIPHERMENT; + else if (pk->pubkey_algo == GCRY_PK_RSA) + cert->key_usage = KEY_DIGITAL_SIGNATURE | KEY_KEY_ENCIPHERMENT; + } + + cert->params_size = cdk_pk_get_npkey (pk->pubkey_algo); + for (i = 0; i < cert->params_size; i++) + { + nbytes = sizeof (buf) / sizeof (buf[0]); + cdk_pk_get_mpi (pk, i, buf, nbytes, &nbytes, NULL); + rc = _gnutls_mpi_scan_pgp (&cert->params[i], buf, &nbytes); + if (rc) + { + rc = GNUTLS_E_MPI_SCAN_FAILED; + break; + } + } + + if (rc) + release_mpi_array (cert->params, i - 1); + return rc; +} + +/*- + * _gnutls_openpgp_raw_privkey_to_gkey - Converts an OpenPGP secret key to GnuTLS + * @pkey: the GnuTLS private key context to store the key. + * @raw_key: the raw data which contains the whole key packets. + * @format: the format of the key packets. + * + * The RFC2440 (OpenPGP Message Format) data is converted into the + * GnuTLS specific data which is need to perform secret key operations. + * + * This function can read both BASE64 and RAW keys. + -*/ +int +_gnutls_openpgp_raw_privkey_to_gkey (gnutls_privkey * pkey, + const gnutls_datum_t * raw_key, + gnutls_openpgp_crt_fmt_t format) +{ + cdk_kbnode_t snode = NULL; + cdk_packet_t pkt; + cdk_stream_t out; + cdk_pkt_seckey_t sk = NULL; + int pke_algo, i, j; + size_t nbytes = 0; + uint8_t buf[512]; + int rc = 0; + + if (!pkey || raw_key->size <= 0) + { + gnutls_assert (); + return GNUTLS_E_CERTIFICATE_ERROR; + } + + rc = cdk_stream_tmp_new (&out); + if (rc) + return GNUTLS_E_CERTIFICATE_ERROR; + + if (format == GNUTLS_OPENPGP_FMT_BASE64) + { + rc = cdk_stream_set_armor_flag (out, 0); + if (rc) + { + cdk_stream_close (out); + rc = _gnutls_map_cdk_rc (rc); + gnutls_assert (); + goto leave; + } + } + + cdk_stream_write (out, raw_key->data, raw_key->size); + cdk_stream_seek (out, 0); + + rc = cdk_keydb_get_keyblock (out, &snode); + cdk_stream_close (out); + if (rc) + { + rc = GNUTLS_E_OPENPGP_GETKEY_FAILED; + goto leave; + } + + pkt = cdk_kbnode_find_packet (snode, CDK_PKT_SECRET_KEY); + if (!pkt) + { + rc = GNUTLS_E_OPENPGP_GETKEY_FAILED; + goto leave; + } + sk = pkt->pkt.secret_key; + pke_algo = sk->pk->pubkey_algo; + pkey->params_size = cdk_pk_get_npkey (pke_algo); + for (i = 0; i < pkey->params_size; i++) + { + nbytes = sizeof (buf) / sizeof (buf[0]); + cdk_pk_get_mpi (sk->pk, i, buf, nbytes, &nbytes, NULL); + rc = _gnutls_mpi_scan_pgp (&pkey->params[i], buf, &nbytes); + if (rc) + { + rc = GNUTLS_E_MPI_SCAN_FAILED; + release_mpi_array (pkey->params, i - 1); + goto leave; + } + } + + pkey->params_size += cdk_pk_get_nskey (pke_algo); + for (j = 0; j < cdk_pk_get_nskey (pke_algo); j++, i++) + { + nbytes = sizeof (buf) / sizeof (buf[0]); + cdk_sk_get_mpi (sk, j, buf, nbytes, &nbytes, NULL); + rc = _gnutls_mpi_scan_pgp (&pkey->params[i], buf, &nbytes); + if (rc) + { + rc = GNUTLS_E_MPI_SCAN_FAILED; + release_mpi_array (pkey->params, i - 1); + goto leave; + } + } + + if (is_ELG (pke_algo)) + return GNUTLS_E_UNWANTED_ALGORITHM; + else if (is_RSA (pke_algo)) + pkey->pk_algorithm = GNUTLS_PK_RSA; + +leave: + cdk_kbnode_release (snode); + return rc; +} + + +/*- + * _gnutls_openpgp_raw_key_to_gcert - Converts raw OpenPGP data to GnuTLS certs + * @cert: the certificate to store the data. + * @raw: the buffer which contains the whole OpenPGP key packets. + * + * The RFC2440 (OpenPGP Message Format) data is converted to a GnuTLS + * specific certificate. + -*/ +int +_gnutls_openpgp_raw_key_to_gcert (gnutls_cert * cert, + const gnutls_datum_t * raw) +{ + cdk_kbnode_t knode = NULL; + cdk_packet_t pkt = NULL; + int rc; + + if (!cert) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + memset (cert, 0, sizeof *cert); + + rc = cdk_kbnode_read_from_mem (&knode, raw->data, raw->size); + if (!(rc = _gnutls_map_cdk_rc (rc))) + { + pkt = cdk_kbnode_find_packet (knode, CDK_PKT_PUBLIC_KEY); + } + if (!pkt) + { + gnutls_assert (); + rc = _gnutls_map_cdk_rc (rc); + } + if (!rc) + rc = _gnutls_set_datum (&cert->raw, raw->data, raw->size); + if (!rc) + rc = openpgp_pk_to_gnutls_cert (cert, pkt->pkt.public_key); + + cdk_kbnode_release (knode); + return rc; +} + +/** + * gnutls_certificate_set_openpgp_key - Used to set keys in a gnutls_certificate_credentials_t structure + * @res: is an #gnutls_certificate_credentials_t structure. + * @key: contains an openpgp public key + * @pkey: is an openpgp private key + * + * This function sets a certificate/private key pair in the + * gnutls_certificate_credentials_t structure. This function may be called + * more than once (in case multiple keys/certificates exist for the + * server). + * + **/ +int +gnutls_certificate_set_openpgp_key (gnutls_certificate_credentials_t + res, gnutls_openpgp_crt_t crt, + gnutls_openpgp_privkey_t pkey) +{ + int ret; + + /* this should be first */ + + res->pkey = gnutls_realloc_fast (res->pkey, + (res->ncerts + 1) * + sizeof (gnutls_privkey)); + if (res->pkey == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + ret = _gnutls_openpgp_privkey_to_gkey (&res->pkey[res->ncerts], pkey); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + res->cert_list = gnutls_realloc_fast (res->cert_list, + (1 + + res->ncerts) * + sizeof (gnutls_cert *)); + if (res->cert_list == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + res->cert_list_length = gnutls_realloc_fast (res->cert_list_length, + (1 + + res->ncerts) * sizeof (int)); + if (res->cert_list_length == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + res->cert_list[res->ncerts] = gnutls_calloc (1, sizeof (gnutls_cert)); + if (res->cert_list[res->ncerts] == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + res->cert_list_length[res->ncerts] = 1; + + ret = _gnutls_openpgp_crt_to_gcert (res->cert_list[res->ncerts], crt); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + res->ncerts++; + + /* FIXME: Check if the keys match. */ + + return 0; +} + + +/*- + * gnutls_openpgp_get_key - Retrieve a key from the keyring. + * @key: the destination context to save the key. + * @keyring: the datum struct that contains all keyring information. + * @attr: The attribute (keyid, fingerprint, ...). + * @by: What attribute is used. + * + * This function can be used to retrieve keys by different pattern + * from a binary or a file keyring. + -*/ +int +gnutls_openpgp_get_key (gnutls_datum_t * key, + gnutls_openpgp_keyring_t keyring, key_attr_t by, + opaque * pattern) +{ + cdk_kbnode_t knode = NULL; + unsigned long keyid[2]; + unsigned char *buf; + void *desc; + size_t len; + int rc = 0; + + if (!key || !keyring || by == KEY_ATTR_NONE) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + memset (key, 0, sizeof *key); + + if (by == KEY_ATTR_SHORT_KEYID) + { + keyid[0] = buftou32 (pattern); + desc = keyid; + } + else if (by == KEY_ATTR_KEYID) + { + keyid[0] = buftou32 (pattern); + keyid[1] = buftou32 (pattern + 4); + desc = keyid; + } + else + desc = pattern; + rc = cdk_keydb_search_start (keyring->db, by, desc); + if (!rc) + rc = cdk_keydb_search (keyring->db, &knode); + if (rc) + { + rc = _gnutls_map_cdk_rc (rc); + goto leave; + } + + if (!cdk_kbnode_find (knode, CDK_PKT_PUBLIC_KEY)) + { + rc = GNUTLS_E_OPENPGP_GETKEY_FAILED; + goto leave; + } + + /* We let the function allocate the buffer to avoid + to call the function twice. */ + rc = cdk_kbnode_write_to_mem_alloc (knode, &buf, &len); + if (!rc) + datum_append (key, buf, len); + cdk_free (buf); + +leave: + cdk_kbnode_release (knode); + return rc; +} + + +/* Convert the stream to a datum. In this case we use the mmap + function to map the entire stream to a buffer. */ +static int +stream_to_datum (cdk_stream_t inp, gnutls_datum_t * raw) +{ + uint8_t *buf; + size_t buflen; + + if (!inp || !raw) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + cdk_stream_mmap (inp, &buf, &buflen); + datum_append (raw, buf, buflen); + cdk_free (buf); + + if (!buflen) + { + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + return 0; +} + + + +/** + * gnutls_certificate_set_openpgp_key_mem - Used to set OpenPGP keys + * @res: the destination context to save the data. + * @cert: the datum that contains the public key. + * @key: the datum that contains the secret key. + * + * This funtion is used to load OpenPGP keys into the GnuTLS credential + * structure. + * It doesn't matter whether the keys are armored or not, but the files + * should only contain one key which should not be encrypted. + **/ +int +gnutls_certificate_set_openpgp_key_mem (gnutls_certificate_credentials_t + res, const gnutls_datum_t * icert, + const gnutls_datum_t * ikey, + gnutls_openpgp_crt_fmt_t format) +{ + gnutls_openpgp_privkey_t key; + gnutls_openpgp_crt_t cert; + int ret; + + ret = gnutls_openpgp_privkey_init (&key); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + ret = gnutls_openpgp_privkey_import (key, ikey, format, NULL, 0); + if (ret < 0) + { + gnutls_assert (); + gnutls_openpgp_privkey_deinit (key); + return ret; + } + + ret = gnutls_openpgp_crt_init (&cert); + if (ret < 0) + { + gnutls_assert (); + gnutls_openpgp_privkey_deinit (key); + return ret; + } + + ret = gnutls_openpgp_crt_import (cert, icert, format); + if (ret < 0) + { + gnutls_assert (); + gnutls_openpgp_privkey_deinit (key); + gnutls_openpgp_crt_deinit (cert); + return ret; + } + + + ret = gnutls_certificate_set_openpgp_key (res, cert, key); + + gnutls_openpgp_privkey_deinit (key); + gnutls_openpgp_crt_deinit (cert); + + return ret; +} + + +/** + * gnutls_certificate_set_openpgp_key_file - Used to set OpenPGP keys + * @res: the destination context to save the data. + * @certfile: the file that contains the public key. + * @keyfile: the file that contains the secret key. + * + * This funtion is used to load OpenPGP keys into the GnuTLS credentials structure. + * It doesn't matter whether the keys are armored or not, but the files + * should only contain one key which should not be encrypted. + **/ +int +gnutls_certificate_set_openpgp_key_file (gnutls_certificate_credentials_t + res, const char *certfile, + const char *keyfile, + gnutls_openpgp_crt_fmt_t format) +{ + struct stat statbuf; + gnutls_datum_t key, cert; + int rc; + size_t size; + + if (!res || !keyfile || !certfile) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + if (stat (certfile, &statbuf) || stat (keyfile, &statbuf)) + { + gnutls_assert (); + return GNUTLS_E_FILE_ERROR; + } + + cert.data = read_binary_file (certfile, &size); + cert.size = (unsigned int) size; + if (cert.data == NULL) + { + gnutls_assert (); + return GNUTLS_E_FILE_ERROR; + } + + key.data = read_binary_file (keyfile, &size); + key.size = (unsigned int) size; + if (key.data == NULL) + { + gnutls_assert (); + free (cert.data); + return GNUTLS_E_FILE_ERROR; + } + + rc = gnutls_certificate_set_openpgp_key_mem (res, &cert, &key, format); + + free (cert.data); + free (key.data); + + if (rc < 0) + { + gnutls_assert (); + return rc; + } + + return 0; +} + + +int +gnutls_openpgp_count_key_names (const gnutls_datum_t * cert) +{ + cdk_kbnode_t knode, p, ctx; + cdk_packet_t pkt; + int nuids; + + if (cert == NULL) + { + gnutls_assert (); + return 0; + } + + if (cdk_kbnode_read_from_mem (&knode, cert->data, cert->size)) + { + gnutls_assert (); + return 0; + } + + ctx = NULL; + for (nuids = 0;;) + { + p = cdk_kbnode_walk (knode, &ctx, 0); + if (!p) + break; + pkt = cdk_kbnode_get_packet (p); + if (pkt->pkttype == CDK_PKT_USER_ID) + nuids++; + } + + cdk_kbnode_release (knode); + return nuids; +} + + +/** + * gnutls_certificate_set_openpgp_keyring_file - Sets a keyring file for OpenPGP + * @c: A certificate credentials structure + * @file: filename of the keyring. + * + * The function is used to set keyrings that will be used internally + * by various OpenPGP functions. For example to find a key when it + * is needed for an operations. The keyring will also be used at the + * verification functions. + * + **/ +int +gnutls_certificate_set_openpgp_keyring_file (gnutls_certificate_credentials_t + c, const char *file, + gnutls_openpgp_crt_fmt_t format) +{ + gnutls_datum_t ring; + size_t size; + int rc; + + if (!c || !file) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + ring.data = read_binary_file (file, &size); + ring.size = (unsigned int) size; + if (ring.data == NULL) + { + gnutls_assert (); + return GNUTLS_E_FILE_ERROR; + } + + rc = + gnutls_certificate_set_openpgp_keyring_mem (c, ring.data, ring.size, + format); + + free (ring.data); + + return rc; +} + +/** + * gnutls_certificate_set_openpgp_keyring_mem - Add keyring data for OpenPGP + * @c: A certificate credentials structure + * @data: buffer with keyring data. + * @dlen: length of data buffer. + * + * The function is used to set keyrings that will be used internally + * by various OpenPGP functions. For example to find a key when it + * is needed for an operations. The keyring will also be used at the + * verification functions. + * + **/ +int +gnutls_certificate_set_openpgp_keyring_mem (gnutls_certificate_credentials_t + c, const opaque * data, + size_t dlen, + gnutls_openpgp_crt_fmt_t format) +{ +#ifndef KEYRING_HACK + cdk_stream_t inp; + size_t count; + uint8_t *buf; + gnutls_datum ddata; + int rc; + + ddata.data = (void *) data; + ddata.size = dlen; + + if (!c || !data || !dlen) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + rc = gnutls_openpgp_keyring_init (&c->keyring); + if (rc < 0) + { + gnutls_assert (); + return rc; + } + + rc = gnutls_openpgp_keyring_import (c->keyring, &ddata, format); + if (rc < 0) + { + gnutls_assert (); + gnutls_openpgp_keyring_deinit (c->keyring); + return rc; + } + + return 0; +#else + + c->keyring_format = format; + + c->keyring.data = gnutls_malloc (dlen + 1); + if (c->keyring.data == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + memcpy (c->keyring.data, data, dlen); + c->keyring.data[dlen] = 0; + c->keyring.size = dlen; + +#endif +} + +/*- + * _gnutls_openpgp_request_key - Receives a key from a database, key server etc + * @ret - a pointer to gnutls_datum_t structure. + * @cred - a gnutls_certificate_credentials_t structure. + * @key_fingerprint - The keyFingerprint + * @key_fingerprint_size - the size of the fingerprint + * + * Retrieves a key from a local database, keyring, or a key server. The + * return value is locally allocated. + * + -*/ +int +_gnutls_openpgp_request_key (gnutls_session_t session, gnutls_datum_t * ret, + const gnutls_certificate_credentials_t cred, + opaque * key_fpr, int key_fpr_size) +{ + int rc = 0; +#ifdef KEYRING_HACK + gnutls_openpgp_keyring_t kring; +#endif + + if (!ret || !cred || !key_fpr) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + if (key_fpr_size != 16 && key_fpr_size != 20) + return GNUTLS_E_HASH_FAILED; /* only MD5 and SHA1 are supported */ + +#ifndef KEYRING_HACK + rc = gnutls_openpgp_get_key (ret, cred->keyring, KEY_ATTR_FPR, key_fpr); +#else + rc = gnutls_openpgp_keyring_init (&kring); + if (rc < 0) + { + gnutls_assert (); + return rc; + } + + rc = + gnutls_openpgp_keyring_import (kring, &cred->keyring, + cred->keyring_format); + if (rc < 0) + { + gnutls_assert (); + gnutls_openpgp_keyring_deinit (kring); + return rc; + } +#endif + if (rc >= 0) /* key was found */ + { + rc = 0; + goto error; + } + else + rc = GNUTLS_E_OPENPGP_GETKEY_FAILED; + + /* If the callback function was set, then try this one. */ + if (session->internals.openpgp_recv_key_func != NULL) + { + rc = session->internals.openpgp_recv_key_func (session, + key_fpr, + key_fpr_size, ret); + if (rc < 0) + { + gnutls_assert (); + rc = GNUTLS_E_OPENPGP_GETKEY_FAILED; + goto error; + } + } + +error: +#ifdef KEYRING_HACK + gnutls_openpgp_keyring_deinit (kring); +#endif + return rc; +} + +/** + * gnutls_openpgp_set_recv_key_function - Used to set a key retrieval callback for PGP keys + * @session: a TLS session + * @func: the callback + * + * This funtion will set a key retrieval function for OpenPGP keys. This + * callback is only useful in server side, and will be used if the peer + * sent a key fingerprint instead of a full key. + * + **/ +void +gnutls_openpgp_set_recv_key_function (gnutls_session_t session, + gnutls_openpgp_recv_key_func func) +{ + session->internals.openpgp_recv_key_func = func; +} + + +/* Copies a gnutls_openpgp_privkey_t to a gnutls_privkey structure. */ +int +_gnutls_openpgp_privkey_to_gkey (gnutls_privkey * dest, + gnutls_openpgp_privkey_t src) +{ + int i, ret; + + memset (dest, 0, sizeof (gnutls_privkey)); + + for (i = 0; i < src->pkey.params_size; i++) + { + dest->params[i] = _gnutls_mpi_copy (src->pkey.params[i]); + if (dest->params[i] == NULL) + { + gnutls_assert (); + ret = GNUTLS_E_MEMORY_ERROR; + goto cleanup; + } + } + + dest->pk_algorithm = src->pkey.pk_algorithm; + dest->params_size = src->pkey.params_size; + + return 0; + +cleanup: + for (i = 0; i < src->pkey.params_size; i++) + _gnutls_mpi_release (&dest->params[i]); + return ret; +} + +/* Converts a parsed gnutls_openpgp_crt_t to a gnutls_cert structure. + */ +int +_gnutls_openpgp_crt_to_gcert (gnutls_cert * gcert, gnutls_openpgp_crt_t cert) +{ + opaque *der; + size_t der_size = 0; + gnutls_datum_t raw; + int ret; + + memset (gcert, 0, sizeof (gnutls_cert)); + gcert->cert_type = GNUTLS_CRT_OPENPGP; + + + ret = gnutls_openpgp_crt_export (cert, GNUTLS_OPENPGP_FMT_RAW, + NULL, &der_size); + if (ret != GNUTLS_E_SHORT_MEMORY_BUFFER) + { + gnutls_assert (); + return ret; + } + + der = gnutls_malloc (der_size); + if (der == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + ret = gnutls_openpgp_crt_export (cert, GNUTLS_OPENPGP_FMT_RAW, + der, &der_size); + if (ret < 0) + { + gnutls_assert (); + gnutls_free (der); + return ret; + } + + raw.data = der; + raw.size = der_size; + + ret = _gnutls_openpgp_raw_key_to_gcert (gcert, &raw); + if (ret < 0) + { + gnutls_assert (); + gnutls_free (der); + return ret; + } + + gnutls_free (der); + + return 0; + +} + + +/** + * gnutls_openpgp_privkey_sign_hash - This function will sign the given data using the private key params + * @key: Holds the key + * @hash: holds the data to be signed + * @signature: will contain newly allocated signature + * + * This function will sign the given hash using the private key. + * + * Return value: In case of failure a negative value will be returned, + * and 0 on success. + **/ +int +gnutls_openpgp_privkey_sign_hash (gnutls_openpgp_privkey_t key, + const gnutls_datum_t * hash, + gnutls_datum_t * signature) +{ + int result; + + if (key == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + result = _gnutls_sign (key->pkey.pk_algorithm, key->pkey.params, + key->pkey.params_size, hash, signature); + if (result < 0) + { + gnutls_assert (); + return result; + } + + return 0; +} diff --git a/src/daemon/https/openpgp/gnutls_openpgp.h b/src/daemon/https/openpgp/gnutls_openpgp.h new file mode 100644 index 00000000..82e22eee --- /dev/null +++ b/src/daemon/https/openpgp/gnutls_openpgp.h @@ -0,0 +1,98 @@ +#include + +#ifdef ENABLE_OPENPGP + +#ifndef GNUTLS_OPENPGP_H +#define GNUTLS_OPENPGP_H + +#include +#include + +typedef struct + { + int type; + size_t size; + uint8_t *data; + }keybox_blob; + +typedef enum + { + KBX_BLOB_FILE = 0x00, + KBX_BLOB_DATA = 0x01 + }keyring_blob_types; + +/* OpenCDK compatible */ +typedef enum + { + KEY_ATTR_NONE = 0, + KEY_ATTR_SHORT_KEYID = 3, + KEY_ATTR_KEYID = 4, + KEY_ATTR_FPR = 5 + }key_attr_t; + +int +gnutls_certificate_set_openpgp_key_file (gnutls_certificate_credentials_t + res, const char *CERTFILE, + const char *KEYFILE, gnutls_openpgp_crt_fmt_t); + +int gnutls_openpgp_count_key_names (const gnutls_datum_t * cert); + +int gnutls_certificate_set_openpgp_keyring_file +(gnutls_certificate_credentials_t c, const char *file, gnutls_openpgp_crt_fmt_t); + +int +gnutls_certificate_set_openpgp_keyring_mem (gnutls_certificate_credentials_t + c, const opaque * data, + size_t dlen, gnutls_openpgp_crt_fmt_t); + +int gnutls_openpgp_get_key (gnutls_datum_t * key, + gnutls_openpgp_keyring_t keyring, + key_attr_t by, opaque * pattern); + +int gnutls_openpgp_recv_key (const char *host, + short port, uint32_t keyid, + gnutls_datum_t * key); + +/* internal */ +int _gnutls_openpgp_raw_key_to_gcert (gnutls_cert * cert, + const gnutls_datum_t * raw); + +extern int +_gnutls_openpgp_raw_privkey_to_gkey (gnutls_privkey * pkey, + const gnutls_datum_t * raw_key, + gnutls_openpgp_crt_fmt_t format); + +int +_gnutls_openpgp_request_key (gnutls_session_t, + gnutls_datum_t * ret, + const gnutls_certificate_credentials_t cred, + opaque * key_fpr, int key_fpr_size); + +int _gnutls_openpgp_verify_key (const gnutls_certificate_credentials_t, + const gnutls_datum_t * cert_list, + int cert_list_length, unsigned int *status); +int _gnutls_openpgp_fingerprint (const gnutls_datum_t * cert, + unsigned char *fpr, size_t * fprlen); +time_t _gnutls_openpgp_get_raw_key_creation_time (const gnutls_datum_t * + cert); +time_t _gnutls_openpgp_get_raw_key_expiration_time (const gnutls_datum_t * + cert); + +int +gnutls_openpgp_privkey_init (gnutls_openpgp_privkey_t * key); + +int +gnutls_openpgp_privkey_init (gnutls_openpgp_privkey_t * key); + +void +gnutls_openpgp_privkey_deinit (gnutls_openpgp_privkey_t key); + +int +gnutls_openpgp_privkey_import (gnutls_openpgp_privkey_t key, + const gnutls_datum_t * data, + gnutls_openpgp_crt_fmt_t format, + const char *pass, unsigned int flags); + +#endif /*GNUTLS_OPENPGP_H */ + +#endif /*ENABLE_OPENPGP */ diff --git a/src/daemon/https/openpgp/openpgp.h b/src/daemon/https/openpgp/openpgp.h new file mode 100644 index 00000000..e4ea952b --- /dev/null +++ b/src/daemon/https/openpgp/openpgp.h @@ -0,0 +1,182 @@ +#ifndef OPENPGP_H +#define OPENPGP_H + +#include "config.h" + +#ifdef ENABLE_OPENPGP + +#ifdef __cplusplus +extern "C" + { +#endif + +#include +#include +#include "opencdk.h" + +/* Internal context to store the OpenPGP key. */ +typedef struct gnutls_openpgp_crt_int + { + cdk_kbnode_t knode; + } gnutls_openpgp_crt_int; + +/* Internal context to store the private OpenPGP key. */ +typedef struct gnutls_openpgp_privkey_int + { + gnutls_privkey pkey; + } gnutls_openpgp_privkey_int; + +typedef struct gnutls_openpgp_keyring_int + { + cdk_keydb_hd_t db; + cdk_stream_t db_stream; + } gnutls_openpgp_keyring_int; + +typedef struct gnutls_openpgp_keyring_int * gnutls_openpgp_keyring_t; +/* gnutls_openpgp_cert_t should be defined in gnutls.h */ + +/* initializes the memory for gnutls_openpgp_crt_t struct */ +int gnutls_openpgp_crt_init(gnutls_openpgp_crt_t * key); +/* frees all memory */ +void gnutls_openpgp_crt_deinit(gnutls_openpgp_crt_t key); + +int gnutls_openpgp_crt_import(gnutls_openpgp_crt_t key, + const gnutls_datum_t * data, + gnutls_openpgp_crt_fmt_t format); +int gnutls_openpgp_crt_export(gnutls_openpgp_crt_t key, + gnutls_openpgp_crt_fmt_t format, + void *output_data, + size_t * output_data_size); + +/* The key_usage flags are defined in gnutls.h. They are + * the GNUTLS_KEY_* definitions. + */ +int gnutls_openpgp_crt_get_key_usage(gnutls_openpgp_crt_t cert, + unsigned int *key_usage); +int gnutls_openpgp_crt_get_fingerprint(gnutls_openpgp_crt_t key, + void *fpr, + size_t * fprlen); + +int gnutls_openpgp_crt_get_name(gnutls_openpgp_crt_t key, + int idx, + char *buf, + size_t * sizeof_buf); + +gnutls_pk_algorithm_t + gnutls_openpgp_crt_get_pk_algorithm(gnutls_openpgp_crt_t key, + unsigned int *bits); + +int gnutls_openpgp_crt_get_version(gnutls_openpgp_crt_t key); + +time_t gnutls_openpgp_crt_get_creation_time(gnutls_openpgp_crt_t key); +time_t gnutls_openpgp_crt_get_expiration_time(gnutls_openpgp_crt_t key); + +int gnutls_openpgp_crt_get_id(gnutls_openpgp_crt_t key, + unsigned char keyid[8]); + +int gnutls_openpgp_crt_check_hostname(gnutls_openpgp_crt_t key, + const char *hostname); + +/* privkey stuff. */ +int gnutls_openpgp_privkey_init(gnutls_openpgp_privkey_t * key); +void gnutls_openpgp_privkey_deinit(gnutls_openpgp_privkey_t key); +gnutls_pk_algorithm_t + gnutls_openpgp_privkey_get_pk_algorithm(gnutls_openpgp_privkey_t key, + unsigned int *bits); +int gnutls_openpgp_privkey_import(gnutls_openpgp_privkey_t key, + const gnutls_datum_t * data, + gnutls_openpgp_crt_fmt_t format, + const char *pass, + unsigned int flags); +int gnutls_openpgp_privkey_sign_hash(gnutls_openpgp_privkey_t key, + const gnutls_datum_t * hash, + gnutls_datum_t * signature); + +/* Keyring stuff. */ + +int gnutls_openpgp_keyring_init(gnutls_openpgp_keyring_t * keyring); +void gnutls_openpgp_keyring_deinit(gnutls_openpgp_keyring_t keyring); + +int gnutls_openpgp_keyring_import(gnutls_openpgp_keyring_t keyring, + const gnutls_datum_t * data, + gnutls_openpgp_crt_fmt_t format); + +int gnutls_openpgp_keyring_check_id(gnutls_openpgp_keyring_t ring, + const unsigned char keyid[8], + unsigned int flags); + +int gnutls_openpgp_crt_verify_ring(gnutls_openpgp_crt_t key, + gnutls_openpgp_keyring_t keyring, + unsigned int flags, + unsigned int *verify +/* the output of the verification */); + +int gnutls_openpgp_crt_verify_self(gnutls_openpgp_crt_t key, + unsigned int flags, + unsigned int *verify); + +/* certificate authentication stuff. + */ +int gnutls_certificate_set_openpgp_key(gnutls_certificate_credentials_t + res, + gnutls_openpgp_crt_t key, + gnutls_openpgp_privkey_t pkey); + +#ifdef __cplusplus +} +#endif + +int _gnutls_map_cdk_rc(int rc); +int gnutls_openpgp_crt_get_name(gnutls_openpgp_crt_t key, + int idx, + char *buf, + size_t * sizeof_buf); +int gnutls_openpgp_crt_get_fingerprint(gnutls_openpgp_crt_t key, + void *fpr, + size_t * fprlen); +gnutls_pk_algorithm_t + gnutls_openpgp_crt_get_pk_algorithm(gnutls_openpgp_crt_t key, + unsigned int *bits); +int gnutls_openpgp_crt_get_version(gnutls_openpgp_crt_t key); +time_t gnutls_openpgp_crt_get_creation_time(gnutls_openpgp_crt_t key); +time_t gnutls_openpgp_crt_get_expiration_time(gnutls_openpgp_crt_t key); +int gnutls_openpgp_crt_get_id(gnutls_openpgp_crt_t key, + unsigned char keyid[8]); + +int gnutls_openpgp_crt_init(gnutls_openpgp_crt_t * key); +void gnutls_openpgp_crt_deinit(gnutls_openpgp_crt_t key); +int gnutls_openpgp_crt_import(gnutls_openpgp_crt_t key, + const gnutls_datum_t * data, + gnutls_openpgp_crt_fmt_t format); +int gnutls_openpgp_crt_export(gnutls_openpgp_crt_t key, + gnutls_openpgp_crt_fmt_t format, + void *output_data, + size_t * output_data_size); + +void gnutls_openpgp_keyring_deinit(gnutls_openpgp_keyring_t keyring); +int gnutls_openpgp_keyring_init(gnutls_openpgp_keyring_t * keyring); +int gnutls_openpgp_keyring_import(gnutls_openpgp_keyring_t keyring, + const gnutls_datum_t * data, + gnutls_openpgp_crt_fmt_t format); +int gnutls_openpgp_keyring_check_id(gnutls_openpgp_keyring_t ring, + const unsigned char keyid[8], + unsigned int flags); + +int gnutls_openpgp_crt_verify_ring(gnutls_openpgp_crt_t key, + gnutls_openpgp_keyring_t keyring, + unsigned int flags, + unsigned int *verify); + +int gnutls_openpgp_crt_verify_self(gnutls_openpgp_crt_t key, + unsigned int flags, + unsigned int *verify); + +int _gnutls_openpgp_crt_to_gcert(gnutls_cert * gcert, + gnutls_openpgp_crt_t cert); +int _gnutls_openpgp_privkey_to_gkey(gnutls_privkey * dest, + gnutls_openpgp_privkey_t src); + +void gnutls_openpgp_privkey_deinit(gnutls_openpgp_privkey_t key); + +#endif /* ENABLE_OPENPGP */ +#endif /* OPENPGP_H */ diff --git a/src/daemon/https/openpgp/pgp.c b/src/daemon/https/openpgp/pgp.c new file mode 100644 index 00000000..fca307f5 --- /dev/null +++ b/src/daemon/https/openpgp/pgp.c @@ -0,0 +1,534 @@ +/* + * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation + * + * Author: Timo Schulz, Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS-EXTRA. + * + * GNUTLS-EXTRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GNUTLS-EXTRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* Functions on OpenPGP key parsing + */ + +#include +#include +#include +#include +#include "openpgp.h" +/* x509 */ +#include + +/** + * gnutls_openpgp_crt_init - This function initializes a gnutls_openpgp_crt_t structure + * @key: The structure to be initialized + * + * This function will initialize an OpenPGP key structure. + * + * Returns 0 on success. + * + **/ +int gnutls_openpgp_crt_init(gnutls_openpgp_crt_t * key) +{ + *key = gnutls_calloc(1, sizeof(gnutls_openpgp_crt_int)); + + if (*key) + return 0; /* success */ + return GNUTLS_E_MEMORY_ERROR; +} + +/** + * gnutls_openpgp_crt_deinit - This function deinitializes memory used by a gnutls_openpgp_crt_t structure + * @key: The structure to be initialized + * + * This function will deinitialize a key structure. + **/ +void gnutls_openpgp_crt_deinit(gnutls_openpgp_crt_t key) +{ + if (!key) + return; + + if (key->knode) + { + cdk_kbnode_release(key->knode); + key->knode = NULL; + } + + gnutls_free(key); +} + +/** + * gnutls_openpgp_crt_import - This function will import a RAW or BASE64 encoded key + * @key: The structure to store the parsed key. + * @data: The RAW or BASE64 encoded key. + * @format: One of gnutls_openpgp_crt_fmt_t elements. + * + * This function will convert the given RAW or Base64 encoded key + * to the native gnutls_openpgp_crt_t format. The output will be stored in 'key'. + * + * Returns 0 on success. + **/ +int gnutls_openpgp_crt_import(gnutls_openpgp_crt_t key, + const gnutls_datum_t * data, + gnutls_openpgp_crt_fmt_t format) +{ + cdk_stream_t inp; + int rc; + + if (format == GNUTLS_OPENPGP_FMT_RAW) + rc = cdk_kbnode_read_from_mem(&key->knode, data->data, data->size); + else + { + rc = cdk_stream_tmp_from_mem(data->data, data->size, &inp); + if (rc) + { + rc = _gnutls_map_cdk_rc(rc); + gnutls_assert (); + return rc; + } + if (cdk_armor_filter_use(inp)) + rc = cdk_stream_set_armor_flag(inp, 0); + if (!rc) + rc = cdk_keydb_get_keyblock(inp, &key->knode); + cdk_stream_close(inp); + if (rc) + { + rc = _gnutls_map_cdk_rc(rc); + gnutls_assert (); + return rc; + } + } + + return 0; +} + +/** + * gnutls_openpgp_crt_export - This function will export a RAW or BASE64 encoded key + * @key: Holds the key. + * @format: One of gnutls_openpgp_crt_fmt_t elements. + * @output_data: will contain the key base64 encoded or raw + * @output_data_size: holds the size of output_data (and will be replaced by the actual size of parameters) + * + * This function will convert the given key to RAW or Base64 format. + * If the buffer provided is not long enough to hold the output, then + * GNUTLS_E_SHORT_MEMORY_BUFFER will be returned. + * + * Returns 0 on success. + * + **/ +int gnutls_openpgp_crt_export(gnutls_openpgp_crt_t key, + gnutls_openpgp_crt_fmt_t format, + void *output_data, + size_t * output_data_size) +{ + size_t input_data_size = *output_data_size; + size_t calc_size; + int rc; + + rc = cdk_kbnode_write_to_mem(key->knode, output_data, output_data_size); + if (rc) + { + rc = _gnutls_map_cdk_rc(rc); + gnutls_assert (); + return rc; + } + + /* FIXME: The first call of this function is with output_data == NULL + to figure out the size and the caller expects this error here. */ + if (!output_data) + return GNUTLS_E_SHORT_MEMORY_BUFFER; + + if (format == GNUTLS_OPENPGP_FMT_BASE64) + { + unsigned char *in = cdk_calloc(1, *output_data_size); + memcpy(in, output_data, *output_data_size); + + /* Calculate the size of the encoded data and check if the provided + buffer is large enough. */ + rc = cdk_armor_encode_buffer(in, input_data_size, + NULL, 0, &calc_size, CDK_ARMOR_PUBKEY); + if (rc || calc_size > input_data_size) + { + cdk_free(in); + *output_data_size = calc_size; + rc = _gnutls_map_cdk_rc(CDK_Too_Short); + gnutls_assert (); + return rc; + } + + rc = cdk_armor_encode_buffer(in, input_data_size, output_data, + input_data_size, &calc_size, + CDK_ARMOR_PUBKEY); + cdk_free(in); + *output_data_size = calc_size; + } + + return 0; +} + +/** + * gnutls_openpgp_crt_get_fingerprint - Gets the fingerprint + * @key: the raw data that contains the OpenPGP public key. + * @fpr: the buffer to save the fingerprint, must hold at least 20 bytes. + * @fprlen: the integer to save the length of the fingerprint. + * + * Returns the fingerprint of the OpenPGP key. Depends on the algorithm, + * the fingerprint can be 16 or 20 bytes. + **/ +int gnutls_openpgp_crt_get_fingerprint(gnutls_openpgp_crt_t key, + void *fpr, + size_t * fprlen) +{ + cdk_packet_t pkt; + cdk_pkt_pubkey_t pk= NULL; + + if (!fpr || !fprlen) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + *fprlen = 0; + + pkt = cdk_kbnode_find_packet(key->knode, CDK_PKT_PUBLIC_KEY); + if (!pkt) + return GNUTLS_E_OPENPGP_GETKEY_FAILED; + + pk = pkt->pkt.public_key; + *fprlen = 20; + + /* FIXME: Check if the draft allows old PGP keys. */ + if (is_RSA (pk->pubkey_algo) && pk->version < 4) + *fprlen = 16; + cdk_pk_get_fingerprint(pk, fpr); + + return 0; +} + +int _gnutls_openpgp_count_key_names(gnutls_openpgp_crt_t key) +{ + cdk_kbnode_t p, ctx; + cdk_packet_t pkt; + int nuids; + + if (key == NULL) + { + gnutls_assert (); + return 0; + } + + ctx = NULL; + nuids = 0; + while ((p = cdk_kbnode_walk(key->knode, &ctx, 0))) + { + pkt = cdk_kbnode_get_packet(p); + if (pkt->pkttype == CDK_PKT_USER_ID) + nuids++; + } + + return nuids; +} + +/** + * gnutls_openpgp_crt_get_name - Extracts the userID + * @key: the structure that contains the OpenPGP public key. + * @idx: the index of the ID to extract + * @buf: a pointer to a structure to hold the name + * @sizeof_buf: holds the maximum size of @buf, on return hold the + * actual/required size of @buf. + * + * Extracts the userID from the parsed OpenPGP key. + * + * Returns 0 on success, and GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE + * if the index of the ID does not exist. + * + **/ +int gnutls_openpgp_crt_get_name(gnutls_openpgp_crt_t key, + int idx, + char *buf, + size_t * sizeof_buf) +{ + cdk_kbnode_t ctx= NULL, p; + cdk_packet_t pkt= NULL; + cdk_pkt_userid_t uid= NULL; + int pos = 0; + + if (!key || !buf) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + if (idx < 0 || idx > _gnutls_openpgp_count_key_names(key)) + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + + if (!idx) + pkt = cdk_kbnode_find_packet(key->knode, CDK_PKT_USER_ID); + else + { + pos = 0; + while ((p = cdk_kbnode_walk(key->knode, &ctx, 0))) + { + pkt = cdk_kbnode_get_packet(p); + if (pkt->pkttype == CDK_PKT_USER_ID && ++pos == idx) + break; + } + } + + if (!pkt) + { + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + uid = pkt->pkt.user_id; + if (uid->len >= *sizeof_buf) + { + gnutls_assert (); + *sizeof_buf = uid->len + 1; + return GNUTLS_E_SHORT_MEMORY_BUFFER; + } + + memcpy(buf, uid->name, uid->len); + buf[uid->len] = '\0'; /* make sure it's a string */ + *sizeof_buf = uid->len + 1; + + if (uid->is_revoked) + return GNUTLS_E_OPENPGP_UID_REVOKED; + + return 0; +} + +/** + * gnutls_openpgp_crt_get_pk_algorithm - This function returns the key's PublicKey algorithm + * @key: is an OpenPGP key + * @bits: if bits is non null it will hold the size of the parameters' in bits + * + * This function will return the public key algorithm of an OpenPGP + * certificate. + * + * If bits is non null, it should have enough size to hold the parameters + * size in bits. For RSA the bits returned is the modulus. + * For DSA the bits returned are of the public exponent. + * + * Returns a member of the GNUTLS_PKAlgorithm enumeration on success, + * or a negative value on error. + * + **/ +gnutls_pk_algorithm_t gnutls_openpgp_crt_get_pk_algorithm(gnutls_openpgp_crt_t key, + unsigned int *bits) +{ + cdk_packet_t pkt; + int algo; + + if (!key) + return GNUTLS_PK_UNKNOWN; + + algo = 0; + pkt = cdk_kbnode_find_packet(key->knode, CDK_PKT_PUBLIC_KEY); + if (pkt && pkt->pkttype == CDK_PKT_PUBLIC_KEY) + { + if (bits) + *bits = cdk_pk_get_nbits(pkt->pkt.public_key); + algo = pkt->pkt.public_key->pubkey_algo; + if (is_RSA (algo)) + algo = GNUTLS_PK_RSA; + else + algo = GNUTLS_E_UNKNOWN_PK_ALGORITHM; + } + + return algo; +} + +/** + * gnutls_openpgp_crt_get_version - Extracts the version of the key. + * @key: the structure that contains the OpenPGP public key. + * + * Extract the version of the OpenPGP key. + **/ +int gnutls_openpgp_crt_get_version(gnutls_openpgp_crt_t key) +{ + cdk_packet_t pkt; + int version; + + if (!key) + return -1; + + pkt = cdk_kbnode_find_packet(key->knode, CDK_PKT_PUBLIC_KEY); + if (pkt) + version = pkt->pkt.public_key->version; + else + version = 0; + + return version; +} + +/** + * gnutls_openpgp_crt_get_creation_time - Extract the timestamp + * @key: the structure that contains the OpenPGP public key. + * + * Returns the timestamp when the OpenPGP key was created. + **/ +time_t gnutls_openpgp_crt_get_creation_time(gnutls_openpgp_crt_t key) +{ + cdk_packet_t pkt; + time_t timestamp; + + if (!key) + return (time_t) - 1; + + pkt = cdk_kbnode_find_packet(key->knode, CDK_PKT_PUBLIC_KEY); + if (pkt) + timestamp = pkt->pkt.public_key->timestamp; + else + timestamp = 0; + + return timestamp; +} + +/** + * gnutls_openpgp_crt_get_expiration_time - Extract the expire date + * @key: the structure that contains the OpenPGP public key. + * + * Returns the time when the OpenPGP key expires. A value of '0' means + * that the key doesn't expire at all. + **/ +time_t gnutls_openpgp_crt_get_expiration_time(gnutls_openpgp_crt_t key) +{ + cdk_packet_t pkt; + time_t expiredate; + + if (!key) + return (time_t) - 1; + + pkt = cdk_kbnode_find_packet(key->knode, CDK_PKT_PUBLIC_KEY); + if (pkt) + expiredate = pkt->pkt.public_key->expiredate; + else + expiredate = 0; + + return expiredate; +} + +/** + * gnutls_openpgp_crt_get_id - Gets the keyID + * @key: the structure that contains the OpenPGP public key. + * @keyid: the buffer to save the keyid. + * + * Returns the 64-bit keyID of the OpenPGP key. + **/ +int gnutls_openpgp_crt_get_id(gnutls_openpgp_crt_t key, + unsigned char keyid[8]) +{ + cdk_packet_t pkt; + uint32_t kid[2]; + + if (!key || !keyid) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + pkt = cdk_kbnode_find_packet(key->knode, CDK_PKT_PUBLIC_KEY); + if (!pkt) + return GNUTLS_E_OPENPGP_GETKEY_FAILED; + + cdk_pk_get_keyid(pkt->pkt.public_key, kid); + keyid[0] = kid[0] >> 24; + keyid[1] = kid[0] >> 16; + keyid[2] = kid[0] >> 8; + keyid[3] = kid[0]; + keyid[4] = kid[1] >> 24; + keyid[5] = kid[1] >> 16; + keyid[6] = kid[1] >> 8; + keyid[7] = kid[1]; + + return 0; +} + +/** + * gnutls_openpgp_crt_check_hostname - This function compares the given hostname with the hostname in the key + * @key: should contain an gnutls_openpgp_crt_t structure + * @hostname: A null terminated string that contains a DNS name + * + * This function will check if the given key's owner matches + * the given hostname. This is a basic implementation of the matching + * described in RFC2818 (HTTPS), which takes into account wildcards. + * + * Returns non zero on success, and zero on failure. + * + **/ +int gnutls_openpgp_crt_check_hostname(gnutls_openpgp_crt_t key, + const char *hostname) +{ + char dnsname[MAX_CN]; + size_t dnsnamesize; + int ret; + int i; + + /* Check through all included names. */ + for (i = 0; !(ret < 0); i++) + { + dnsnamesize = sizeof (dnsname); + ret = gnutls_openpgp_crt_get_name(key, i, dnsname, &dnsnamesize); + /* FIXME: ret is not used */ + if (_gnutls_hostname_compare(dnsname, hostname)) + return 1; + } + + /* not found a matching name */ + return 0; +} + +/** + * gnutls_openpgp_crt_get_key_usage - This function returns the key's usage + * @key: should contain a gnutls_openpgp_crt_t structure + * @key_usage: where the key usage bits will be stored + * + * This function will return certificate's key usage, by checking the + * key algorithm. The key usage value will ORed values of the: + * GNUTLS_KEY_DIGITAL_SIGNATURE, GNUTLS_KEY_KEY_ENCIPHERMENT. + * + * A negative value may be returned in case of parsing error. + * + */ +int gnutls_openpgp_crt_get_key_usage(gnutls_openpgp_crt_t key, + unsigned int *key_usage) +{ + cdk_packet_t pkt; + int algo = 0; + + if (!key) + return GNUTLS_E_INVALID_REQUEST; + + *key_usage = 0; + + pkt = cdk_kbnode_find_packet(key->knode, CDK_PKT_PUBLIC_KEY); + if (pkt && pkt->pkttype == CDK_PKT_PUBLIC_KEY) + { + algo = pkt->pkt.public_key->pubkey_algo; + + /* FIXME: We need to take a look at the key flags because + RSA-E and RSA-S are obsolete. Only RSA is used + and the flags are used to set the capabilities. */ + if (is_DSA (algo) || algo == GCRY_PK_RSA_S) + *key_usage |= KEY_DIGITAL_SIGNATURE; + else if (algo == GCRY_PK_RSA_E) + *key_usage |= KEY_KEY_ENCIPHERMENT; + else if (algo == GCRY_PK_RSA) + *key_usage |= KEY_DIGITAL_SIGNATURE | KEY_KEY_ENCIPHERMENT; + } + + return 0; +} diff --git a/src/daemon/https/openpgp/pgp_privkey.c b/src/daemon/https/openpgp/pgp_privkey.c new file mode 100644 index 00000000..e09697eb --- /dev/null +++ b/src/daemon/https/openpgp/pgp_privkey.c @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2006, 2007 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS-EXTRA. + * + * GNUTLS-EXTRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GNUTLS-EXTRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* Functions on OpenPGP privkey parsing + */ + +#include +#include +#include +#include +#include "openpgp.h" +#include +#include +/* x509 */ +#include + +/** + * gnutls_openpgp_privkey_init - This function initializes a gnutls_openpgp_privkey_t structure + * @key: The structure to be initialized + * + * This function will initialize an OpenPGP key structure. + * + * Returns 0 on success. + * + **/ +int +gnutls_openpgp_privkey_init (gnutls_openpgp_privkey_t * key) +{ + *key = gnutls_calloc (1, sizeof (gnutls_openpgp_privkey_int)); + + if (*key) + return 0; /* success */ + return GNUTLS_E_MEMORY_ERROR; +} + +/** + * gnutls_openpgp_privkey_deinit - This function deinitializes memory used by a gnutls_openpgp_privkey_t structure + * @key: The structure to be initialized + * + * This function will deinitialize a key structure. + * + **/ +void +gnutls_openpgp_privkey_deinit (gnutls_openpgp_privkey_t key) +{ + if (!key) + return; + + _gnutls_gkey_deinit (&key->pkey); + gnutls_free (key); +} + +/** + * gnutls_openpgp_privkey_import - This function will import a RAW or BASE64 encoded key + * @key: The structure to store the parsed key. + * @data: The RAW or BASE64 encoded key. + * @format: One of gnutls_openpgp_crt_fmt_t elements. + * @pass: Unused for now + * @flags: should be zero + * + * This function will convert the given RAW or Base64 encoded key + * to the native gnutls_openpgp_privkey_t format. The output will be stored in 'key'. + * + * Returns 0 on success. + * + **/ +int +gnutls_openpgp_privkey_import (gnutls_openpgp_privkey_t key, + const gnutls_datum_t * data, + gnutls_openpgp_crt_fmt_t format, + const char *pass, unsigned int flags) +{ + int rc; + + rc = _gnutls_openpgp_raw_privkey_to_gkey (&key->pkey, data, format); + if (rc) + { + gnutls_assert (); + return rc; + } + + return 0; +} + + +/** + * gnutls_openpgp_privkey_get_pk_algorithm - This function returns the key's PublicKey algorithm + * @key: is an OpenPGP key + * @bits: if bits is non null it will hold the size of the parameters' in bits + * + * This function will return the public key algorithm of an OpenPGP + * certificate. + * + * If bits is non null, it should have enough size to hold the parameters + * size in bits. For RSA the bits returned is the modulus. + * For DSA the bits returned are of the public exponent. + * + * Returns a member of the GNUTLS_PKAlgorithm enumeration on success, + * or a negative value on error. + * + **/ +gnutls_pk_algorithm_t +gnutls_openpgp_privkey_get_pk_algorithm (gnutls_openpgp_privkey_t key, + unsigned int *bits) +{ + int pk = key->pkey.pk_algorithm; + + if (bits) + { + *bits = 0; + if (pk == GNUTLS_PK_RSA) + *bits = _gnutls_mpi_get_nbits (key->pkey.params[0]); + } + + return pk; +} diff --git a/src/daemon/https/openpgp/pgp_verify.c b/src/daemon/https/openpgp/pgp_verify.c new file mode 100644 index 00000000..cfa29b69 --- /dev/null +++ b/src/daemon/https/openpgp/pgp_verify.c @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2002, 2003, 2004, 2005, 2007 Free Software Foundation + * + * Author: Timo Schulz, Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS-EXTRA. + * + * GNUTLS-EXTRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GNUTLS-EXTRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* Functions on OpenPGP key parsing + */ + +#include +#include +#include +#include +#include "openpgp.h" +/* x509 */ +#include /* lib/x509/verify.h */ + + +/** + * gnutls_openpgp_crt_verify_ring - Verify all signatures in the key + * @key: the structure that holds the key. + * @keyring: holds the keyring to check against + * @flags: unused (should be 0) + * @verify: will hold the certificate verification output. + * + * Verify all signatures in the key, using the given set of keys (keyring). + * + * The key verification output will be put in @verify and will be + * one or more of the gnutls_certificate_status_t enumerated elements bitwise or'd. + * + * GNUTLS_CERT_INVALID: A signature on the key is invalid. + * + * GNUTLS_CERT_REVOKED: The key has been revoked. + * + * Note that this function does not verify using any "web of + * trust". You may use GnuPG for that purpose, or any other external + * PGP application. + * + * Returns 0 on success. + **/ +int +gnutls_openpgp_crt_verify_ring (gnutls_openpgp_crt_t key, + gnutls_openpgp_keyring_t keyring, + unsigned int flags, unsigned int *verify) +{ + opaque id[8]; + cdk_error_t rc; + int status; + + if (!key || !keyring) + { + gnutls_assert (); + return GNUTLS_E_NO_CERTIFICATE_FOUND; + } + + *verify = 0; + + rc = cdk_pk_check_sigs (key->knode, keyring->db, &status); + if (rc == CDK_Error_No_Key) + { + rc = GNUTLS_E_NO_CERTIFICATE_FOUND; + gnutls_assert (); + return rc; + } + else if (rc != CDK_Success) + { + _gnutls_x509_log ("cdk_pk_check_sigs: error %d\n", rc); + rc = _gnutls_map_cdk_rc (rc); + gnutls_assert (); + return rc; + } + _gnutls_x509_log ("status: %x\n", status); + + if (status & CDK_KEY_INVALID) + *verify |= GNUTLS_CERT_INVALID; + if (status & CDK_KEY_REVOKED) + *verify |= GNUTLS_CERT_REVOKED; + if (status & CDK_KEY_NOSIGNER) + *verify |= GNUTLS_CERT_SIGNER_NOT_FOUND; + + /* Check if the key is included in the ring. */ + if (!(flags & GNUTLS_VERIFY_DO_NOT_ALLOW_SAME)) + { + rc = gnutls_openpgp_crt_get_id (key, id); + if (rc < 0) + { + gnutls_assert (); + return rc; + } + + rc = gnutls_openpgp_keyring_check_id (keyring, id, 0); + /* If it exists in the keyring don't treat it as unknown. */ + if (rc == 0 && *verify & GNUTLS_CERT_SIGNER_NOT_FOUND) + *verify ^= GNUTLS_CERT_SIGNER_NOT_FOUND; + } + + return 0; +} + + +/** + * gnutls_openpgp_crt_verify_self - Verify the self signature on the key + * @key: the structure that holds the key. + * @flags: unused (should be 0) + * @verify: will hold the key verification output. + * + * Verifies the self signature in the key. + * The key verification output will be put in @verify and will be + * one or more of the gnutls_certificate_status_t enumerated elements bitwise or'd. + * + * GNUTLS_CERT_INVALID: The self signature on the key is invalid. + * + * Returns 0 on success. + **/ +int +gnutls_openpgp_crt_verify_self (gnutls_openpgp_crt_t key, + unsigned int flags, unsigned int *verify) +{ + int status; + cdk_error_t rc; + + rc = cdk_pk_check_self_sig (key->knode, &status); + if (rc || status != CDK_KEY_VALID) + *verify |= GNUTLS_CERT_INVALID; + else + *verify = 0; + + return 0; +} diff --git a/src/daemon/https/tests.c b/src/daemon/https/tests.c new file mode 100644 index 00000000..35fc70e9 --- /dev/null +++ b/src/daemon/https/tests.c @@ -0,0 +1,1160 @@ +/* + * Copyright (C) 2004, 2006, 2007 Free Software Foundation + * Copyright (C) 2000,2001,2002,2003 Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * GNUTLS is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GNUTLS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include + +#ifndef _WIN32 +# include +# include +#else +# include +#endif + +#include +#include +#include +#include +#include + +extern gnutls_srp_client_credentials_t srp_cred; +extern gnutls_anon_client_credentials_t anon_cred; +extern gnutls_certificate_credentials_t xcred; + +extern int verbose; + +int tls1_ok = 0; +int ssl3_ok = 0; +int tls1_1_ok = 0; + +/* keep session info */ +static char *session_data = NULL; +static char session_id[32]; +static size_t session_data_size = 0, session_id_size = 0; +static int sfree = 0; +static int handshake_output = 0; + +int +do_handshake (gnutls_session_t session) +{ + int ret, alert; + + do + { + ret = gnutls_handshake (session); + } + while (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN); + + handshake_output = ret; + + if (ret < 0 && verbose > 1) + { + if (ret == GNUTLS_E_WARNING_ALERT_RECEIVED + || ret == GNUTLS_E_FATAL_ALERT_RECEIVED) + { + alert = gnutls_alert_get (session); + printf ("\n"); + printf ("*** Received alert [%d]: %s\n", + alert, gnutls_alert_get_name (alert)); + } + } + + if (ret < 0) + return TEST_FAILED; + + gnutls_session_get_data (session, NULL, &session_data_size); + + if (sfree != 0) + { + free (session_data); + sfree = 0; + } + session_data = malloc (session_data_size); + sfree = 1; + if (session_data == NULL) + { + fprintf (stderr, "Memory error\n"); + exit (1); + } + gnutls_session_get_data (session, session_data, &session_data_size); + + session_id_size = sizeof (session_id); + gnutls_session_get_id (session, session_id, &session_id_size); + + return TEST_SUCCEED; +} + +static int protocol_priority[16] = { GNUTLS_TLS1, GNUTLS_SSL3, 0 }; +static const int kx_priority[16] = + { GNUTLS_KX_RSA, GNUTLS_KX_DHE_DSS, GNUTLS_KX_DHE_RSA, + GNUTLS_KX_ANON_DH, + GNUTLS_KX_RSA_EXPORT, 0 +}; +static const int cipher_priority[16] = + { GNUTLS_CIPHER_3DES_CBC, GNUTLS_CIPHER_ARCFOUR_128, + GNUTLS_CIPHER_ARCFOUR_40, 0 +}; +static const int comp_priority[16] = { GNUTLS_COMP_NULL, 0 }; +static const int mac_priority[16] = { GNUTLS_MAC_SHA1, GNUTLS_MAC_MD5, 0 }; +static const int cert_type_priority[16] = { GNUTLS_CRT_X509, 0 }; + +#define ADD_ALL_CIPHERS(session) gnutls_cipher_set_priority(session, cipher_priority) +#define ADD_ALL_COMP(session) gnutls_compression_set_priority(session, comp_priority) +#define ADD_ALL_MACS(session) gnutls_mac_set_priority(session, mac_priority) +#define ADD_ALL_KX(session) gnutls_kx_set_priority(session, kx_priority) +#define ADD_ALL_PROTOCOLS(session) gnutls_protocol_set_priority(session, protocol_priority) +#define ADD_ALL_CERTTYPES(session) gnutls_certificate_type_set_priority(session, cert_type_priority) + +static void +ADD_KX (gnutls_session_t session, int kx) +{ + static int _kx_priority[] = { 0, 0 }; + _kx_priority[0] = kx; + + gnutls_kx_set_priority (session, _kx_priority); +} + +static void +ADD_KX2 (gnutls_session_t session, int kx1, int kx2) +{ + static int _kx_priority[] = { 0, 0, 0 }; + _kx_priority[0] = kx1; + _kx_priority[1] = kx2; + + gnutls_kx_set_priority (session, _kx_priority); +} + +static void +ADD_CIPHER (gnutls_session_t session, int cipher) +{ + static int _cipher_priority[] = { 0, 0 }; + _cipher_priority[0] = cipher; + + gnutls_cipher_set_priority (session, _cipher_priority); +} + +static void +ADD_CIPHER4 (gnutls_session_t session, int cipher1, int cipher2, int cipher3, + int cipher4) +{ + static int _cipher_priority[] = { 0, 0, 0, 0, 0 }; + _cipher_priority[0] = cipher1; + _cipher_priority[1] = cipher2; + _cipher_priority[2] = cipher3; + _cipher_priority[3] = cipher4; + + gnutls_cipher_set_priority (session, _cipher_priority); +} + +static void +ADD_MAC (gnutls_session_t session, int mac) +{ + static int _mac_priority[] = { 0, 0 }; + _mac_priority[0] = mac; + + gnutls_mac_set_priority (session, _mac_priority); +} + +static void +ADD_COMP (gnutls_session_t session, int c) +{ + static int _comp_priority[] = { 0, 0 }; + _comp_priority[0] = c; + + gnutls_compression_set_priority (session, _comp_priority); +} + +static void +ADD_CERTTYPE (gnutls_session_t session, int ctype) +{ + static int _ct_priority[] = { 0, 0 }; + _ct_priority[0] = ctype; + + gnutls_certificate_type_set_priority (session, _ct_priority); +} + +static void +ADD_PROTOCOL (gnutls_session_t session, int protocol) +{ + static int _proto_priority[] = { 0, 0 }; + _proto_priority[0] = protocol; + + gnutls_protocol_set_priority (session, _proto_priority); +} + +static void +ADD_PROTOCOL3 (gnutls_session_t session, int p1, int p2, int p3) +{ + static int _proto_priority[] = { 0, 0, 0, 0 }; + _proto_priority[0] = p1; + _proto_priority[1] = p2; + _proto_priority[2] = p3; + + gnutls_protocol_set_priority (session, _proto_priority); +} + +#ifdef ENABLE_SRP +static int srp_detected; + +int +_test_srp_username_callback (gnutls_session_t session, + char **username, char **password) +{ + srp_detected = 1; + + return -1; +} + +test_code_t +test_srp (gnutls_session_t session) +{ + int ret; + + ADD_ALL_CIPHERS (session); + ADD_ALL_COMP (session); + ADD_ALL_CERTTYPES (session); + ADD_ALL_PROTOCOLS (session); + ADD_ALL_MACS (session); + + ADD_KX (session, GNUTLS_KX_SRP); + srp_detected = 0; + + gnutls_srp_set_client_credentials_function (srp_cred, + _test_srp_username_callback); + + gnutls_credentials_set (session, GNUTLS_CRD_SRP, srp_cred); + + ret = do_handshake (session); + + gnutls_srp_set_client_credentials_function (srp_cred, NULL); + + if (srp_detected != 0) + return TEST_SUCCEED; + else + return TEST_FAILED; +} +#endif + +test_code_t +test_server (gnutls_session_t session) +{ + int ret, i = 0; + char buf[5 * 1024]; + char *p; + const char snd_buf[] = "GET / HTTP/1.0\n\n"; + + if (verbose == 0) + return TEST_UNSURE; + + buf[sizeof (buf) - 1] = 0; + + ADD_ALL_CIPHERS (session); + ADD_ALL_COMP (session); + ADD_ALL_CERTTYPES (session); + ADD_ALL_PROTOCOLS (session); + ADD_ALL_MACS (session); + ADD_ALL_KX (session); + + gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, xcred); + + ret = do_handshake (session); + if (ret != TEST_SUCCEED) + return TEST_FAILED; + + gnutls_record_send (session, snd_buf, sizeof (snd_buf) - 1); + ret = gnutls_record_recv (session, buf, sizeof (buf) - 1); + if (ret < 0) + return TEST_FAILED; + + p = strstr (buf, "Server:"); + if (p != NULL) + p = strchr (p, ':'); + if (p != NULL) + { + p++; + while (*p != 0 && *p != '\r' && *p != '\n') + { + putc (*p, stdout); + p++; + i++; + if (i > 128) + break; + } + } + + return TEST_SUCCEED; +} + + +static int export_true = 0; +static gnutls_datum_t exp = { NULL, 0 }, mod = + +{ +NULL, 0}; + +test_code_t +test_export (gnutls_session_t session) +{ + int ret; + + ADD_ALL_COMP (session); + ADD_ALL_CERTTYPES (session); + ADD_ALL_PROTOCOLS (session); + ADD_ALL_MACS (session); + + ADD_KX (session, GNUTLS_KX_RSA_EXPORT); + ADD_CIPHER (session, GNUTLS_CIPHER_ARCFOUR_40); + gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, xcred); + + ret = do_handshake (session); + + if (ret == TEST_SUCCEED) + { + export_true = 1; + gnutls_rsa_export_get_pubkey (session, &exp, &mod); + } + + return ret; +} + +test_code_t +test_export_info (gnutls_session_t session) +{ + int ret2, ret; + gnutls_datum_t exp2, mod2; + const char *print; + + if (verbose == 0 || export_true == 0) + return TEST_IGNORE; + + ADD_ALL_COMP (session); + ADD_ALL_CERTTYPES (session); + ADD_ALL_PROTOCOLS (session); + ADD_ALL_MACS (session); + + ADD_KX (session, GNUTLS_KX_RSA_EXPORT); + ADD_CIPHER (session, GNUTLS_CIPHER_ARCFOUR_40); + gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, xcred); + + ret = do_handshake (session); + + if (ret == TEST_SUCCEED) + { + ret2 = gnutls_rsa_export_get_pubkey (session, &exp2, &mod2); + if (ret2 >= 0) + { + printf ("\n"); + + print = raw_to_string (exp2.data, exp2.size); + if (print) + printf (" Exponent [%d bits]: %s\n", exp2.size * 8, print); + + print = raw_to_string (mod2.data, mod2.size); + if (print) + printf (" Modulus [%d bits]: %s\n", mod2.size * 8, print); + + if (mod2.size != mod.size || exp2.size != exp.size || + memcmp (mod2.data, mod.data, mod.size) != 0 || + memcmp (exp2.data, exp.data, exp.size) != 0) + { + printf + (" (server uses different public keys per connection)\n"); + } + } + } + + return ret; + +} + +static gnutls_datum_t pubkey = { NULL, 0 }; + +test_code_t +test_dhe (gnutls_session_t session) +{ + int ret; + + ADD_ALL_CIPHERS (session); + ADD_ALL_COMP (session); + ADD_ALL_CERTTYPES (session); + ADD_ALL_PROTOCOLS (session); + ADD_ALL_MACS (session); + + ADD_KX2 (session, GNUTLS_KX_DHE_RSA, GNUTLS_KX_DHE_DSS); + gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, xcred); + + ret = do_handshake (session); + + gnutls_dh_get_pubkey (session, &pubkey); + + return ret; +} + +test_code_t +test_dhe_group (gnutls_session_t session) +{ + int ret, ret2; + gnutls_datum_t gen, prime, pubkey2; + const char *print; + + if (verbose == 0 || pubkey.data == NULL) + return TEST_IGNORE; + + ADD_ALL_CIPHERS (session); + ADD_ALL_COMP (session); + ADD_ALL_CERTTYPES (session); + ADD_ALL_PROTOCOLS (session); + ADD_ALL_MACS (session); + + ADD_KX2 (session, GNUTLS_KX_DHE_RSA, GNUTLS_KX_DHE_DSS); + gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, xcred); + + ret = do_handshake (session); + + ret2 = gnutls_dh_get_group (session, &gen, &prime); + if (ret2 >= 0) + { + printf ("\n"); + + print = raw_to_string (gen.data, gen.size); + if (print) + printf (" Generator [%d bits]: %s\n", gen.size * 8, print); + + print = raw_to_string (prime.data, prime.size); + if (print) + printf (" Prime [%d bits]: %s\n", prime.size * 8, print); + + gnutls_dh_get_pubkey (session, &pubkey2); + print = raw_to_string (pubkey2.data, pubkey2.size); + if (print) + printf (" Pubkey [%d bits]: %s\n", pubkey2.size * 8, print); + + if (pubkey2.data && pubkey2.size == pubkey.size && + memcmp (pubkey.data, pubkey2.data, pubkey.size) == 0) + { + printf (" (public key seems to be static among sessions)\n"); + } + } + return ret; +} + +test_code_t +test_ssl3 (gnutls_session_t session) +{ + int ret; + ADD_ALL_CIPHERS (session); + ADD_ALL_COMP (session); + ADD_ALL_CERTTYPES (session); + ADD_PROTOCOL (session, GNUTLS_SSL3); + ADD_ALL_MACS (session); + ADD_ALL_KX (session); + gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, xcred); + + ret = do_handshake (session); + if (ret == TEST_SUCCEED) + ssl3_ok = 1; + + return ret; +} + +static int alrm = 0; +void +got_alarm (int k) +{ + alrm = 1; +} + +test_code_t +test_bye (gnutls_session_t session) +{ + int ret; + char data[20]; + int old, secs = 6; + +#ifndef _WIN32 + signal (SIGALRM, got_alarm); +#endif + + ADD_ALL_CIPHERS (session); + ADD_ALL_COMP (session); + ADD_ALL_CERTTYPES (session); + ADD_ALL_PROTOCOLS (session); + ADD_ALL_MACS (session); + ADD_ALL_KX (session); + gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, xcred); + + ret = do_handshake (session); + if (ret == TEST_FAILED) + return ret; + + ret = gnutls_bye (session, GNUTLS_SHUT_WR); + if (ret < 0) + return TEST_FAILED; + +#ifndef _WIN32 + old = siginterrupt (SIGALRM, 1); + alarm (secs); +#else + setsockopt (gnutls_transport_get_ptr (session), SOL_SOCKET, SO_RCVTIMEO, + (char *) &secs, sizeof (int)); +#endif + + do + { + ret = gnutls_record_recv (session, data, sizeof (data)); + } + while (ret > 0); + +#ifndef _WIN32 + siginterrupt (SIGALRM, old); +#else + if (WSAGetLastError () == WSAETIMEDOUT || + WSAGetLastError () == WSAECONNABORTED) + alrm = 1; +#endif + if (ret == 0) + return TEST_SUCCEED; + + if (alrm == 0) + return TEST_UNSURE; + + return TEST_FAILED; +} + + + +test_code_t +test_aes (gnutls_session_t session) +{ + int ret; + ADD_CIPHER (session, GNUTLS_CIPHER_AES_128_CBC); + ADD_ALL_COMP (session); + ADD_ALL_CERTTYPES (session); + ADD_ALL_PROTOCOLS (session); + ADD_ALL_MACS (session); + ADD_ALL_KX (session); + gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, xcred); + + ret = do_handshake (session); + return ret; +} + +#ifdef ENABLE_CAMELLIA +test_code_t +test_camellia (gnutls_session_t session) +{ + int ret; + ADD_CIPHER (session, GNUTLS_CIPHER_CAMELLIA_128_CBC); + ADD_ALL_COMP (session); + ADD_ALL_CERTTYPES (session); + ADD_ALL_PROTOCOLS (session); + ADD_ALL_MACS (session); + ADD_ALL_KX (session); + gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, xcred); + + ret = do_handshake (session); + return ret; +} +#endif + +test_code_t +test_openpgp1 (gnutls_session_t session) +{ + int ret; + ADD_ALL_CIPHERS (session); + ADD_ALL_COMP (session); + ADD_CERTTYPE (session, GNUTLS_CRT_OPENPGP); + ADD_ALL_PROTOCOLS (session); + ADD_ALL_MACS (session); + ADD_ALL_KX (session); + gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, xcred); + + ret = do_handshake (session); + if (ret == TEST_FAILED) + return ret; + + if (gnutls_certificate_type_get (session) == GNUTLS_CRT_OPENPGP) + return TEST_SUCCEED; + + return TEST_FAILED; +} + +test_code_t +test_unknown_ciphersuites (gnutls_session_t session) +{ + int ret; +#ifdef ENABLE_CAMELLIA + ADD_CIPHER4 (session, GNUTLS_CIPHER_AES_128_CBC, GNUTLS_CIPHER_3DES_CBC, + GNUTLS_CIPHER_CAMELLIA_128_CBC, GNUTLS_CIPHER_ARCFOUR_128); +#else + ADD_CIPHER4 (session, GNUTLS_CIPHER_AES_128_CBC, GNUTLS_CIPHER_3DES_CBC, + GNUTLS_CIPHER_ARCFOUR_128, 0); +#endif + ADD_ALL_COMP (session); + ADD_ALL_CERTTYPES (session); + ADD_ALL_PROTOCOLS (session); + ADD_ALL_MACS (session); + ADD_ALL_KX (session); + gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, xcred); + + ret = do_handshake (session); + return ret; +} + +test_code_t +test_md5 (gnutls_session_t session) +{ + int ret; + ADD_ALL_CIPHERS (session); + ADD_ALL_COMP (session); + ADD_ALL_CERTTYPES (session); + ADD_ALL_PROTOCOLS (session); + ADD_MAC (session, GNUTLS_MAC_MD5); + ADD_ALL_KX (session); + gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, xcred); + + ret = do_handshake (session); + return ret; +} + +#ifdef HAVE_LIBZ +test_code_t +test_zlib (gnutls_session_t session) +{ + int ret; + ADD_ALL_CIPHERS (session); + ADD_COMP (session, GNUTLS_COMP_ZLIB); + ADD_ALL_CERTTYPES (session); + ADD_ALL_PROTOCOLS (session); + ADD_ALL_MACS (session); + ADD_ALL_KX (session); + gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, xcred); + + ret = do_handshake (session); + return ret; +} +#endif + +test_code_t +test_lzo (gnutls_session_t session) +{ + int ret; + gnutls_handshake_set_private_extensions (session, 1); + + ADD_ALL_CIPHERS (session); + ADD_COMP (session, GNUTLS_COMP_LZO); + ADD_ALL_CERTTYPES (session); + ADD_ALL_PROTOCOLS (session); + ADD_ALL_MACS (session); + ADD_ALL_KX (session); + gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, xcred); + + ret = do_handshake (session); + + return ret; +} + +test_code_t +test_sha (gnutls_session_t session) +{ + int ret; + ADD_ALL_CIPHERS (session); + ADD_ALL_COMP (session); + ADD_ALL_CERTTYPES (session); + ADD_ALL_PROTOCOLS (session); + ADD_MAC (session, GNUTLS_MAC_SHA1); + ADD_ALL_KX (session); + gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, xcred); + + ret = do_handshake (session); + return ret; +} + +test_code_t +test_3des (gnutls_session_t session) +{ + int ret; + ADD_CIPHER (session, GNUTLS_CIPHER_3DES_CBC); + ADD_ALL_COMP (session); + ADD_ALL_CERTTYPES (session); + ADD_ALL_PROTOCOLS (session); + ADD_ALL_MACS (session); + ADD_ALL_KX (session); + gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, xcred); + + ret = do_handshake (session); + return ret; +} + +test_code_t +test_arcfour (gnutls_session_t session) +{ + int ret; + ADD_CIPHER (session, GNUTLS_CIPHER_ARCFOUR_128); + ADD_ALL_COMP (session); + ADD_ALL_CERTTYPES (session); + ADD_ALL_PROTOCOLS (session); + ADD_ALL_MACS (session); + ADD_ALL_KX (session); + gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, xcred); + + ret = do_handshake (session); + return ret; +} + +test_code_t +test_arcfour_40 (gnutls_session_t session) +{ + int ret; + ADD_CIPHER (session, GNUTLS_CIPHER_ARCFOUR_40); + ADD_ALL_COMP (session); + ADD_ALL_CERTTYPES (session); + ADD_ALL_PROTOCOLS (session); + ADD_ALL_MACS (session); + ADD_ALL_KX (session); + gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, xcred); + + ret = do_handshake (session); + return ret; +} + +test_code_t +test_tls1 (gnutls_session_t session) +{ + int ret; + ADD_ALL_CIPHERS (session); + ADD_ALL_COMP (session); + ADD_ALL_CERTTYPES (session); + ADD_PROTOCOL (session, GNUTLS_TLS1); + ADD_ALL_MACS (session); + ADD_ALL_KX (session); + gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, xcred); + + ret = do_handshake (session); + if (ret == TEST_SUCCEED) + tls1_ok = 1; + + return ret; + +} + +test_code_t +test_tls1_1 (gnutls_session_t session) +{ + int ret; + ADD_ALL_CIPHERS (session); + ADD_ALL_COMP (session); + ADD_ALL_CERTTYPES (session); + ADD_PROTOCOL (session, GNUTLS_TLS1_1); + ADD_ALL_MACS (session); + ADD_ALL_KX (session); + gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, xcred); + + ret = do_handshake (session); + if (ret == TEST_SUCCEED) + tls1_1_ok = 1; + + return ret; + +} + +test_code_t +test_tls1_1_fallback (gnutls_session_t session) +{ + int ret; + if (tls1_1_ok) + return TEST_IGNORE; + + ADD_ALL_CIPHERS (session); + ADD_ALL_COMP (session); + ADD_ALL_CERTTYPES (session); + ADD_PROTOCOL3 (session, GNUTLS_TLS1_1, GNUTLS_TLS1, GNUTLS_SSL3); + ADD_ALL_MACS (session); + ADD_ALL_KX (session); + gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, xcred); + + ret = do_handshake (session); + if (ret != TEST_SUCCEED) + return TEST_FAILED; + + if (gnutls_protocol_get_version (session) == GNUTLS_TLS1) + return TEST_SUCCEED; + else if (gnutls_protocol_get_version (session) == GNUTLS_SSL3) + return TEST_UNSURE; + + return TEST_FAILED; + +} + +/* Advertize both TLS 1.0 and SSL 3.0. If the connection fails, + * but the previous SSL 3.0 test succeeded then disable TLS 1.0. + */ +test_code_t +test_tls_disable (gnutls_session_t session) +{ + int ret; + if (tls1_ok != 0) + return TEST_IGNORE; + + ADD_ALL_CIPHERS (session); + ADD_ALL_COMP (session); + ADD_ALL_CERTTYPES (session); + ADD_ALL_PROTOCOLS (session); + ADD_ALL_MACS (session); + ADD_ALL_KX (session); + gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, xcred); + + ret = do_handshake (session); + if (ret == TEST_FAILED) + { + /* disable TLS 1.0 */ + if (ssl3_ok != 0) + { + protocol_priority[0] = GNUTLS_SSL3; + protocol_priority[1] = 0; + } + } + return ret; + +} + +test_code_t +test_rsa_pms (gnutls_session_t session) +{ + int ret; + + /* here we enable both SSL 3.0 and TLS 1.0 + * and try to connect and use rsa authentication. + * If the server is old, buggy and only supports + * SSL 3.0 then the handshake will fail. + */ + ADD_ALL_CIPHERS (session); + ADD_ALL_COMP (session); + ADD_ALL_CERTTYPES (session); + ADD_ALL_PROTOCOLS (session); + ADD_ALL_MACS (session); + ADD_KX (session, GNUTLS_KX_RSA); + gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, xcred); + + ret = do_handshake (session); + if (ret == TEST_FAILED) + return TEST_FAILED; + + if (gnutls_protocol_get_version (session) == GNUTLS_TLS1) + return TEST_SUCCEED; + return TEST_UNSURE; +} + +test_code_t +test_max_record_size (gnutls_session_t session) +{ + int ret; + ADD_ALL_CIPHERS (session); + ADD_ALL_COMP (session); + ADD_ALL_CERTTYPES (session); + ADD_ALL_PROTOCOLS (session); + ADD_ALL_MACS (session); + ADD_ALL_KX (session); + gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, xcred); + gnutls_record_set_max_size (session, 512); + + ret = do_handshake (session); + if (ret == TEST_FAILED) + return ret; + + ret = gnutls_record_get_max_size (session); + if (ret == 512) + return TEST_SUCCEED; + + return TEST_FAILED; +} + +test_code_t +test_hello_extension (gnutls_session_t session) +{ + int ret; + ADD_ALL_CIPHERS (session); + ADD_ALL_COMP (session); + ADD_ALL_CERTTYPES (session); + ADD_ALL_PROTOCOLS (session); + ADD_ALL_MACS (session); + ADD_ALL_KX (session); + gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, xcred); + gnutls_record_set_max_size (session, 512); + + ret = do_handshake (session); + return ret; +} + +void _gnutls_record_set_default_version (gnutls_session_t session, + unsigned char major, + unsigned char minor); + +test_code_t +test_version_rollback (gnutls_session_t session) +{ + int ret; + if (tls1_ok == 0) + return TEST_IGNORE; + + /* here we enable both SSL 3.0 and TLS 1.0 + * and we connect using a 3.1 client hello version, + * and a 3.0 record version. Some implementations + * are buggy (and vulnerable to man in the middle + * attacks which allow a version downgrade) and this + * connection will fail. + */ + ADD_ALL_CIPHERS (session); + ADD_ALL_COMP (session); + ADD_ALL_CERTTYPES (session); + ADD_ALL_PROTOCOLS (session); + ADD_ALL_MACS (session); + ADD_ALL_KX (session); + gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, xcred); + _gnutls_record_set_default_version (session, 3, 0); + + ret = do_handshake (session); + if (ret != TEST_SUCCEED) + return ret; + + if (tls1_ok != 0 && gnutls_protocol_get_version (session) == GNUTLS_SSL3) + return TEST_FAILED; + + return TEST_SUCCEED; +} + +/* See if the server tolerates out of bounds + * record layer versions in the first client hello + * message. + */ +test_code_t +test_version_oob (gnutls_session_t session) +{ + int ret; + /* here we enable both SSL 3.0 and TLS 1.0 + * and we connect using a 5.5 record version. + */ + ADD_ALL_CIPHERS (session); + ADD_ALL_COMP (session); + ADD_ALL_CERTTYPES (session); + ADD_ALL_PROTOCOLS (session); + ADD_ALL_MACS (session); + ADD_ALL_KX (session); + gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, xcred); + _gnutls_record_set_default_version (session, 5, 5); + + ret = do_handshake (session); + return ret; +} + +void _gnutls_rsa_pms_set_version (gnutls_session_t session, + unsigned char major, unsigned char minor); + +test_code_t +test_rsa_pms_version_check (gnutls_session_t session) +{ + int ret; + /* here we use an arbitary version in the RSA PMS + * to see whether to server will check this version. + * + * A normal server would abort this handshake. + */ + ADD_ALL_CIPHERS (session); + ADD_ALL_COMP (session); + ADD_ALL_CERTTYPES (session); + ADD_ALL_PROTOCOLS (session); + ADD_ALL_MACS (session); + ADD_ALL_KX (session); + gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, xcred); + _gnutls_rsa_pms_set_version (session, 5, 5); /* use SSL 5.5 version */ + + ret = do_handshake (session); + return ret; + +} + +#ifdef ENABLE_ANON +test_code_t +test_anonymous (gnutls_session_t session) +{ + int ret; + + ADD_ALL_CIPHERS (session); + ADD_ALL_COMP (session); + ADD_ALL_CERTTYPES (session); + ADD_ALL_PROTOCOLS (session); + ADD_ALL_MACS (session); + ADD_KX (session, GNUTLS_KX_ANON_DH); + gnutls_credentials_set (session, GNUTLS_CRD_ANON, anon_cred); + + ret = do_handshake (session); + + if (ret == TEST_SUCCEED) + gnutls_dh_get_pubkey (session, &pubkey); + + return ret; +} +#endif + +test_code_t +test_session_resume2 (gnutls_session_t session) +{ + int ret; + char tmp_session_id[32]; + int tmp_session_id_size; + + if (session == NULL) + return TEST_IGNORE; + + ADD_ALL_CIPHERS (session); + ADD_ALL_COMP (session); + ADD_ALL_CERTTYPES (session); + ADD_ALL_PROTOCOLS (session); + ADD_ALL_MACS (session); + ADD_ALL_KX (session); + + gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, xcred); + gnutls_credentials_set (session, GNUTLS_CRD_ANON, anon_cred); + + gnutls_session_set_data (session, session_data, session_data_size); + + memcpy (tmp_session_id, session_id, session_id_size); + tmp_session_id_size = session_id_size; + + ret = do_handshake (session); + if (ret == TEST_FAILED) + return ret; + + /* check if we actually resumed the previous session */ + + session_id_size = sizeof (session_id); + gnutls_session_get_id (session, session_id, &session_id_size); + + if (session_id_size == 0) + return TEST_FAILED; + + if (gnutls_session_is_resumed (session)) + return TEST_SUCCEED; + + if (tmp_session_id_size == session_id_size && + memcmp (tmp_session_id, session_id, tmp_session_id_size) == 0) + return TEST_SUCCEED; + else + return TEST_FAILED; +} + +extern char *hostname; + +test_code_t +test_certificate (gnutls_session_t session) +{ + int ret; + + if (verbose == 0) + return TEST_IGNORE; + + ADD_ALL_CIPHERS (session); + ADD_ALL_COMP (session); + ADD_ALL_CERTTYPES (session); + ADD_ALL_PROTOCOLS (session); + ADD_ALL_MACS (session); + ADD_ALL_KX (session); + + gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, xcred); + + ret = do_handshake (session); + if (ret == TEST_FAILED) + return ret; + + printf ("\n"); + print_cert_info (session, hostname); + + return TEST_SUCCEED; +} + +/* A callback function to be used at the certificate selection time. + */ +static int +cert_callback (gnutls_session_t session, + const gnutls_datum_t * req_ca_rdn, int nreqs, + const gnutls_pk_algorithm_t * sign_algos, + int sign_algos_length, gnutls_retr_st * st) +{ + char issuer_dn[256]; + int i, ret; + size_t len; + + if (verbose == 0) + return -1; + + /* Print the server's trusted CAs + */ + printf ("\n"); + if (nreqs > 0) + printf ("- Server's trusted authorities:\n"); + else + printf ("- Server did not send us any trusted authorities names.\n"); + + /* print the names (if any) */ + for (i = 0; i < nreqs; i++) + { + len = sizeof (issuer_dn); + ret = gnutls_x509_rdn_get (&req_ca_rdn[i], issuer_dn, &len); + if (ret >= 0) + { + printf (" [%d]: ", i); + printf ("%s\n", issuer_dn); + } + } + + return -1; + +} + +/* Prints the trusted server's CAs. This is only + * if the server sends a certificate request packet. + */ +test_code_t +test_server_cas (gnutls_session_t session) +{ + int ret; + + if (verbose == 0) + return TEST_IGNORE; + + ADD_ALL_CIPHERS (session); + ADD_ALL_COMP (session); + ADD_ALL_CERTTYPES (session); + ADD_ALL_PROTOCOLS (session); + ADD_ALL_MACS (session); + ADD_ALL_KX (session); + + gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, xcred); + gnutls_certificate_client_set_retrieve_function (xcred, cert_callback); + + ret = do_handshake (session); + gnutls_certificate_client_set_retrieve_function (xcred, NULL); + + if (ret == TEST_FAILED) + return ret; + return TEST_SUCCEED; +} diff --git a/src/daemon/https/tests.h b/src/daemon/https/tests.h new file mode 100644 index 00000000..2f27f850 --- /dev/null +++ b/src/daemon/https/tests.h @@ -0,0 +1,42 @@ +typedef enum +{ + TEST_SUCCEED, TEST_FAILED, TEST_UNSURE, TEST_IGNORE +} test_code_t; + +test_code_t test_srp (gnutls_session_t state); +test_code_t test_server (gnutls_session_t state); +test_code_t test_export (gnutls_session_t state); +test_code_t test_export_info (gnutls_session_t state); +test_code_t test_hello_extension (gnutls_session_t state); +test_code_t test_dhe (gnutls_session_t state); +test_code_t test_dhe_group (gnutls_session_t state); +test_code_t test_ssl3 (gnutls_session_t state); +test_code_t test_aes (gnutls_session_t state); +#ifdef ENABLE_CAMELLIA +test_code_t test_camellia (gnutls_session_t state); +#endif +test_code_t test_md5 (gnutls_session_t state); +test_code_t test_sha (gnutls_session_t state); +test_code_t test_3des (gnutls_session_t state); +test_code_t test_arcfour (gnutls_session_t state); +test_code_t test_arcfour_40 (gnutls_session_t state); +test_code_t test_tls1 (gnutls_session_t state); +test_code_t test_tls1_1 (gnutls_session_t state); +test_code_t test_tls1_1_fallback (gnutls_session_t state); +test_code_t test_tls_disable (gnutls_session_t state); +test_code_t test_rsa_pms (gnutls_session_t state); +test_code_t test_max_record_size (gnutls_session_t state); +test_code_t test_version_rollback (gnutls_session_t state); +test_code_t test_anonymous (gnutls_session_t state); +test_code_t test_unknown_ciphersuites (gnutls_session_t state); +test_code_t test_openpgp1 (gnutls_session_t state); +test_code_t test_bye (gnutls_session_t state); +test_code_t test_certificate (gnutls_session_t state); +test_code_t test_server_cas (gnutls_session_t state); +test_code_t test_session_resume2 (gnutls_session_t state); +test_code_t test_rsa_pms_version_check (gnutls_session_t session); +test_code_t test_version_oob (gnutls_session_t session); +test_code_t test_zlib (gnutls_session_t session); +test_code_t test_lzo (gnutls_session_t session); +int _test_srp_username_callback (gnutls_session_t session, + char **username, char **password); diff --git a/src/daemon/https/tls/Makefile.am b/src/daemon/https/tls/Makefile.am new file mode 100644 index 00000000..309080ea --- /dev/null +++ b/src/daemon/https/tls/Makefile.am @@ -0,0 +1,73 @@ +SUBDIRS = . + +AM_CPPFLAGS = \ +-I$(top_srcdir)/src/daemon/https/includes \ +-I$(top_srcdir)/src/daemon/https/lgl \ +-I$(top_srcdir)/src/daemon/https/x509 \ +-I$(top_srcdir)/src/daemon/https/tls \ +-I$(top_srcdir)/src/daemon/https/openpgp \ +-I$(top_srcdir)/src/daemon/https/opencdk \ +-I$(GCRYPT_CPPFLAGS) + +noinst_LTLIBRARIES = libtls.la + +libtls_la_LDFLAGS = \ + -L$(GCRYPT_LIB_PATH) + +libtls_la_SOURCES = \ +auth_anon.c \ +auth_cert.c \ +auth_dh_common.c \ +auth_dhe.c \ +auth_rsa.c \ +auth_rsa_export.c \ +debug.c \ +ext_cert_type.c \ +ext_inner_application.c \ +ext_max_record.c \ +ext_oprfi.c \ +ext_server_name.c \ +gnutls_alert.c \ +gnutls_algorithms.c \ +gnutls_anon_cred.c \ +gnutls_asn1_tab.c \ +gnutls_auth.c \ +gnutls_buffers.c \ +gnutls_cert.c \ +gnutls_cipher.c \ +gnutls_cipher_int.c \ +gnutls_compress.c \ +gnutls_compress_int.c \ +gnutls_constate.c \ +gnutls_datum.c \ +gnutls_db.c \ +gnutls_dh.c \ +gnutls_dh_primes.c \ +gnutls_errors.c \ +gnutls_extensions.c \ +gnutls_extra_hooks.c \ +gnutls_global.c \ +gnutls_handshake.c \ +gnutls_hash_int.c \ +gnutls_kx.c \ +gnutls_mem.c \ +gnutls_mpi.c \ +gnutls_num.c \ +gnutls_pk.c \ +gnutls_priority.c \ +gnutls_record.c \ +gnutls_rsa_export.c \ +gnutls_session.c \ +gnutls_session_pack.c \ +gnutls_sig.c \ +gnutls_state.c \ +gnutls_str.c \ +gnutls_supplemental.c \ +gnutls_ui.c \ +gnutls_v2_compat.c \ +gnutls_x509.c \ +pkix_asn1_tab.c \ +x509_b64.c + +# gnutlsxx.cpp + diff --git a/src/daemon/https/tls/auth_anon.c b/src/daemon/https/tls/auth_anon.c new file mode 100644 index 00000000..18cad200 --- /dev/null +++ b/src/daemon/https/tls/auth_anon.c @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +/* This file contains the Anonymous Diffie Hellman key exchange part of + * the anonymous authentication. The functions here are used in the + * handshake. + */ + +#include + +#ifdef ENABLE_ANON + +#include "gnutls_auth_int.h" +#include "gnutls_errors.h" +#include "gnutls_dh.h" +#include "auth_anon.h" +#include "gnutls_num.h" +#include "gnutls_mpi.h" +#include +#include + +static int gen_anon_server_kx (gnutls_session_t, opaque **); +static int proc_anon_client_kx (gnutls_session_t, opaque *, size_t); +static int proc_anon_server_kx (gnutls_session_t, opaque *, size_t); + +const mod_auth_st anon_auth_struct = { + "ANON", + NULL, + NULL, + gen_anon_server_kx, + _gnutls_gen_dh_common_client_kx, /* this can be shared */ + NULL, + NULL, + + NULL, + NULL, /* certificate */ + proc_anon_server_kx, + proc_anon_client_kx, + NULL, + NULL +}; + +static int +gen_anon_server_kx (gnutls_session_t session, opaque ** data) +{ + mpi_t g, p; + const mpi_t *mpis; + int ret; + gnutls_dh_params_t dh_params; + gnutls_anon_server_credentials_t cred; + + cred = (gnutls_anon_server_credentials_t) + _gnutls_get_cred (session->key, GNUTLS_CRD_ANON, NULL); + if (cred == NULL) + { + gnutls_assert (); + return GNUTLS_E_INSUFFICIENT_CREDENTIALS; + } + + dh_params = + _gnutls_get_dh_params (cred->dh_params, cred->params_func, session); + mpis = _gnutls_dh_params_to_mpi (dh_params); + if (mpis == NULL) + { + gnutls_assert (); + return GNUTLS_E_NO_TEMPORARY_DH_PARAMS; + } + + p = mpis[0]; + g = mpis[1]; + + if ((ret = + _gnutls_auth_info_set (session, GNUTLS_CRD_ANON, + sizeof (anon_auth_info_st), 1)) < 0) + { + gnutls_assert (); + return ret; + } + + _gnutls_dh_set_group (session, g, p); + + ret = _gnutls_dh_common_print_server_kx (session, g, p, data, 0); + if (ret < 0) + { + gnutls_assert (); + } + + return ret; +} + + +static int +proc_anon_client_kx (gnutls_session_t session, opaque * data, + size_t _data_size) +{ + gnutls_anon_server_credentials_t cred; + int bits; + int ret; + mpi_t p, g; + gnutls_dh_params_t dh_params; + const mpi_t *mpis; + + bits = _gnutls_dh_get_allowed_prime_bits (session); + + cred = (gnutls_anon_server_credentials_t) + _gnutls_get_cred (session->key, GNUTLS_CRD_ANON, NULL); + if (cred == NULL) + { + gnutls_assert (); + return GNUTLS_E_INSUFFICIENT_CREDENTIALS; + } + + dh_params = + _gnutls_get_dh_params (cred->dh_params, cred->params_func, session); + mpis = _gnutls_dh_params_to_mpi (dh_params); + if (mpis == NULL) + { + gnutls_assert (); + return GNUTLS_E_NO_TEMPORARY_DH_PARAMS; + } + + p = mpis[0]; + g = mpis[1]; + + ret = _gnutls_proc_dh_common_client_kx (session, data, _data_size, g, p); + + return ret; + +} + +int +proc_anon_server_kx (gnutls_session_t session, opaque * data, + size_t _data_size) +{ + + int ret; + + /* set auth_info */ + if ((ret = + _gnutls_auth_info_set (session, GNUTLS_CRD_ANON, + sizeof (anon_auth_info_st), 1)) < 0) + { + gnutls_assert (); + return ret; + } + + ret = _gnutls_proc_dh_common_server_kx (session, data, _data_size, 0); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + return 0; +} + +#endif /* ENABLE_ANON */ diff --git a/src/daemon/https/tls/auth_anon.h b/src/daemon/https/tls/auth_anon.h new file mode 100644 index 00000000..b1705f85 --- /dev/null +++ b/src/daemon/https/tls/auth_anon.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2001, 2002, 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +/* this is not to be included by gnutls_anon.c */ +#include +#include + +typedef struct gnutls_anon_server_credentials_st +{ + gnutls_dh_params_t dh_params; + /* this callback is used to retrieve the DH or RSA + * parameters. + */ + gnutls_params_function *params_func; +} anon_server_credentials_st; + +typedef struct gnutls_anon_client_credentials_st +{ + int dummy; +} anon_client_credentials_st; + +typedef struct anon_auth_info_st +{ + dh_info_st dh; +} *anon_auth_info_t; + +typedef struct anon_auth_info_st anon_auth_info_st; diff --git a/src/daemon/https/tls/auth_cert.c b/src/daemon/https/tls/auth_cert.c new file mode 100644 index 00000000..43dcb777 --- /dev/null +++ b/src/daemon/https/tls/auth_cert.c @@ -0,0 +1,1781 @@ +/* + * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +/* The certificate authentication functions which are needed in the handshake, + * and are common to RSA and DHE key exchange, are in this file. + */ + +#include +#include "gnutls_auth_int.h" +#include "gnutls_errors.h" +#include +#include +#include "gnutls_dh.h" +#include "gnutls_num.h" +#include "libtasn1.h" +#include "gnutls_datum.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "debug.h" + +static gnutls_cert *alloc_and_load_x509_certs (gnutls_x509_crt_t * certs, + unsigned); +static gnutls_privkey *alloc_and_load_x509_key (gnutls_x509_privkey_t key); +static gnutls_cert *alloc_and_load_pgp_certs (gnutls_openpgp_crt_t cert); +static gnutls_privkey *alloc_and_load_pgp_key (const gnutls_openpgp_privkey_t + key); + + +/* Copies data from a internal certificate struct (gnutls_cert) to + * exported certificate struct (cert_auth_info_t) + */ +static int +_gnutls_copy_certificate_auth_info (cert_auth_info_t info, + gnutls_cert * cert, int ncerts) +{ + /* Copy peer's information to auth_info_t + */ + int ret, i, j; + + if (ncerts == 0) + { + info->raw_certificate_list = NULL; + info->ncerts = 0; + return 0; + } + + info->raw_certificate_list = + gnutls_calloc (1, sizeof (gnutls_datum_t) * ncerts); + if (info->raw_certificate_list == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + for (i = 0; i < ncerts; i++) + { + if (cert->raw.size > 0) + { + ret = + _gnutls_set_datum (&info-> + raw_certificate_list[i], + cert[i].raw.data, cert[i].raw.size); + if (ret < 0) + { + gnutls_assert (); + goto clear; + } + } + } + info->ncerts = ncerts; + + return 0; + +clear: + + for (j = 0; j < i; j++) + _gnutls_free_datum (&info->raw_certificate_list[j]); + + gnutls_free (info->raw_certificate_list); + info->raw_certificate_list = NULL; + + return ret; +} + + + + +/* returns 0 if the algo_to-check exists in the pk_algos list, + * -1 otherwise. + */ +inline static int +_gnutls_check_pk_algo_in_list (const gnutls_pk_algorithm_t * + pk_algos, int pk_algos_length, + gnutls_pk_algorithm_t algo_to_check) +{ + int i; + for (i = 0; i < pk_algos_length; i++) + { + if (algo_to_check == pk_algos[i]) + { + return 0; + } + } + return -1; +} + + +/* Returns the issuer's Distinguished name in odn, of the certificate + * specified in cert. + */ +static int +_gnutls_cert_get_issuer_dn (gnutls_cert * cert, gnutls_datum_t * odn) +{ + ASN1_TYPE dn; + int len, result; + int start, end; + + if ((result = asn1_create_element + (_gnutls_get_pkix (), "PKIX1.Certificate", &dn)) != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + result = asn1_der_decoding (&dn, cert->raw.data, cert->raw.size, NULL); + if (result != ASN1_SUCCESS) + { + /* couldn't decode DER */ + gnutls_assert (); + asn1_delete_structure (&dn); + return _gnutls_asn2err (result); + } + + result = asn1_der_decoding_startEnd (dn, cert->raw.data, cert->raw.size, + "tbsCertificate.issuer", &start, &end); + + if (result != ASN1_SUCCESS) + { + /* couldn't decode DER */ + gnutls_assert (); + asn1_delete_structure (&dn); + return _gnutls_asn2err (result); + } + asn1_delete_structure (&dn); + + len = end - start + 1; + + odn->size = len; + odn->data = &cert->raw.data[start]; + + return 0; +} + + +/* Locates the most appropriate x509 certificate using the + * given DN. If indx == -1 then no certificate was found. + * + * That is to guess which certificate to use, based on the + * CAs and sign algorithms supported by the peer server. + */ +static int +_find_x509_cert (const gnutls_certificate_credentials_t cred, + opaque * _data, size_t _data_size, + const gnutls_pk_algorithm_t * pk_algos, + int pk_algos_length, int *indx) +{ + unsigned size; + gnutls_datum_t odn; + opaque *data = _data; + ssize_t data_size = _data_size; + unsigned i, j; + int result, cert_pk; + + *indx = -1; + + do + { + + DECR_LENGTH_RET (data_size, 2, 0); + size = _gnutls_read_uint16 (data); + DECR_LENGTH_RET (data_size, size, 0); + data += 2; + + for (i = 0; i < cred->ncerts; i++) + { + for (j = 0; j < cred->cert_list_length[i]; j++) + { + if ((result = + _gnutls_cert_get_issuer_dn (&cred-> + cert_list[i][j], &odn)) < 0) + { + gnutls_assert (); + return result; + } + + if (odn.size != size) + continue; + + /* If the DN matches and + * the *_SIGN algorithm matches + * the cert is our cert! + */ + cert_pk = cred->cert_list[i][0].subject_pk_algorithm; + + if ((memcmp (odn.data, data, size) == 0) && + (_gnutls_check_pk_algo_in_list + (pk_algos, pk_algos_length, cert_pk) == 0)) + { + *indx = i; + break; + } + } + if (*indx != -1) + break; + } + + if (*indx != -1) + break; + + /* move to next record */ + data += size; + + } + while (1); + + return 0; + +} + +/* Locates the most appropriate openpgp cert + */ +static int +_find_openpgp_cert (const gnutls_certificate_credentials_t cred, + gnutls_pk_algorithm_t * pk_algos, + int pk_algos_length, int *indx) +{ + unsigned i, j; + + *indx = -1; + + for (i = 0; i < cred->ncerts; i++) + { + for (j = 0; j < cred->cert_list_length[i]; j++) + { + + /* If the *_SIGN algorithm matches + * the cert is our cert! + */ + if ((_gnutls_check_pk_algo_in_list + (pk_algos, pk_algos_length, + cred->cert_list[i][0].subject_pk_algorithm) == 0) + && (cred->cert_list[i][0].cert_type == GNUTLS_CRT_OPENPGP)) + { + *indx = i; + break; + } + } + if (*indx != -1) + break; + } + + return 0; +} + +/* Returns the number of issuers in the server's + * certificate request packet. + */ +static int +get_issuers_num (gnutls_session_t session, opaque * data, ssize_t data_size) +{ + int issuers_dn_len = 0, result; + unsigned size; + + /* Count the number of the given issuers; + * This is used to allocate the issuers_dn without + * using realloc(). + */ + + if (data_size == 0 || data == NULL) + return 0; + + if (data_size > 0) + do + { + /* This works like DECR_LEN() + */ + result = GNUTLS_E_UNEXPECTED_PACKET_LENGTH; + DECR_LENGTH_COM (data_size, 2, goto error); + size = _gnutls_read_uint16 (data); + + result = GNUTLS_E_UNEXPECTED_PACKET_LENGTH; + DECR_LENGTH_COM (data_size, size, goto error); + + data += 2; + + if (size > 0) + { + issuers_dn_len++; + data += size; + } + + if (data_size == 0) + break; + + } + while (1); + + return issuers_dn_len; + +error: + return result; +} + +/* Returns the issuers in the server's certificate request + * packet. + */ +static int +get_issuers (gnutls_session_t session, + gnutls_datum_t * issuers_dn, int issuers_len, + opaque * data, size_t data_size) +{ + int i; + unsigned size; + + if (gnutls_certificate_type_get (session) != GNUTLS_CRT_X509) + return 0; + + /* put the requested DNs to req_dn, only in case + * of X509 certificates. + */ + if (issuers_len > 0) + { + + for (i = 0; i < issuers_len; i++) + { + /* The checks here for the buffer boundaries + * are not needed since the buffer has been + * parsed above. + */ + data_size -= 2; + + size = _gnutls_read_uint16 (data); + + data += 2; + + issuers_dn[i].data = data; + issuers_dn[i].size = size; + + data += size; + } + } + + return 0; +} + +/* Calls the client get callback. + */ +static int +call_get_cert_callback (gnutls_session_t session, + gnutls_datum_t * issuers_dn, + int issuers_dn_length, + gnutls_pk_algorithm_t * pk_algos, int pk_algos_length) +{ + unsigned i; + gnutls_cert *local_certs = NULL; + gnutls_privkey *local_key = NULL; + gnutls_retr_st st; + int ret; + gnutls_certificate_type_t type = gnutls_certificate_type_get (session); + gnutls_certificate_credentials_t cred; + + cred = (gnutls_certificate_credentials_t) + _gnutls_get_cred (session->key, GNUTLS_CRD_CERTIFICATE, NULL); + if (cred == NULL) + { + gnutls_assert (); + return GNUTLS_E_INSUFFICIENT_CREDENTIALS; + } + + memset (&st, 0, sizeof (st)); + + if (session->security_parameters.entity == GNUTLS_SERVER) + { + ret = cred->server_get_cert_callback (session, &st); + } + else + { /* CLIENT */ + ret = + cred->client_get_cert_callback (session, + issuers_dn, issuers_dn_length, + pk_algos, pk_algos_length, &st); + } + + if (ret < 0) + { + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + if (st.ncerts == 0) + return 0; /* no certificate was selected */ + + if (type != st.type) + { + gnutls_assert (); + ret = GNUTLS_E_INVALID_REQUEST; + goto cleanup; + } + + if (type == GNUTLS_CRT_X509) + { + local_certs = alloc_and_load_x509_certs (st.cert.x509, st.ncerts); + if (local_certs != NULL) + local_key = alloc_and_load_x509_key (st.key.x509); + + } + else + { /* PGP */ + if (st.ncerts > 1) + { + gnutls_assert (); + ret = GNUTLS_E_INVALID_REQUEST; + goto cleanup; + } + + local_certs = alloc_and_load_pgp_certs (st.cert.pgp); + if (local_certs != NULL) + local_key = alloc_and_load_pgp_key (st.key.pgp); + + } + + _gnutls_selected_certs_set (session, local_certs, + (local_certs != NULL) ? st.ncerts : 0, + local_key, 1); + + ret = 0; + +cleanup: + + if (st.type == GNUTLS_CRT_X509) + { + if (st.deinit_all) + { + for (i = 0; i < st.ncerts; i++) + { + gnutls_x509_crt_deinit (st.cert.x509[i]); + } + gnutls_free (st.cert.x509); + gnutls_x509_privkey_deinit (st.key.x509); + } + } + else + { + if (st.deinit_all) + { + if (_E_gnutls_openpgp_crt_deinit == NULL || + _E_gnutls_openpgp_privkey_deinit == NULL) + { + gnutls_assert (); + return GNUTLS_E_INIT_LIBEXTRA; + } + + _E_gnutls_openpgp_crt_deinit (st.cert.pgp); + _E_gnutls_openpgp_privkey_deinit (st.key.pgp); + } + } + + return ret; +} + +/* Finds the appropriate certificate depending on the cA Distinguished name + * advertized by the server. If none matches then returns 0 and -1 as index. + * In case of an error a negative value, is returned. + * + * 20020128: added ability to select a certificate depending on the SIGN + * algorithm (only in automatic mode). + */ +static int +_select_client_cert (gnutls_session_t session, + opaque * _data, size_t _data_size, + gnutls_pk_algorithm_t * pk_algos, int pk_algos_length) +{ + int result; + int indx = -1; + gnutls_certificate_credentials_t cred; + opaque *data = _data; + ssize_t data_size = _data_size; + int issuers_dn_length; + gnutls_datum_t *issuers_dn = NULL; + + cred = (gnutls_certificate_credentials_t) + _gnutls_get_cred (session->key, GNUTLS_CRD_CERTIFICATE, NULL); + if (cred == NULL) + { + gnutls_assert (); + return GNUTLS_E_INSUFFICIENT_CREDENTIALS; + } + + if (cred->client_get_cert_callback != NULL) + { + + /* use a callback to get certificate + */ + if (session->security_parameters.cert_type != GNUTLS_CRT_X509) + issuers_dn_length = 0; + else + { + issuers_dn_length = get_issuers_num (session, data, data_size); + if (issuers_dn_length < 0) + { + gnutls_assert (); + return issuers_dn_length; + } + + if (issuers_dn_length > 0) + { + issuers_dn = + gnutls_malloc (sizeof (gnutls_datum_t) * issuers_dn_length); + if (issuers_dn == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + result = + get_issuers (session, issuers_dn, issuers_dn_length, + data, data_size); + if (result < 0) + { + gnutls_assert (); + goto cleanup; + } + } + } + + result = + call_get_cert_callback (session, issuers_dn, issuers_dn_length, + pk_algos, pk_algos_length); + goto cleanup; + + } + else + { + /* If we have no callbacks, try to guess. + */ + result = 0; + + if (session->security_parameters.cert_type == GNUTLS_CRT_X509) + result = + _find_x509_cert (cred, _data, _data_size, + pk_algos, pk_algos_length, &indx); + + if (session->security_parameters.cert_type == GNUTLS_CRT_OPENPGP) + result = _find_openpgp_cert (cred, pk_algos, pk_algos_length, &indx); + + + if (result < 0) + { + gnutls_assert (); + return result; + } + + if (indx >= 0) + { + _gnutls_selected_certs_set (session, + &cred->cert_list[indx][0], + cred->cert_list_length[indx], + &cred->pkey[indx], 0); + } + else + { + _gnutls_selected_certs_set (session, NULL, 0, NULL, 0); + } + + result = 0; + } + +cleanup: + gnutls_free (issuers_dn); + return result; + +} + +/* Generate client certificate + */ + +int +_gnutls_gen_x509_crt (gnutls_session_t session, opaque ** data) +{ + int ret, i; + opaque *pdata; + gnutls_cert *apr_cert_list; + gnutls_privkey *apr_pkey; + int apr_cert_list_length; + + /* find the appropriate certificate + */ + if ((ret = + _gnutls_get_selected_cert (session, &apr_cert_list, + &apr_cert_list_length, &apr_pkey)) < 0) + { + gnutls_assert (); + return ret; + } + + ret = 3; + for (i = 0; i < apr_cert_list_length; i++) + { + ret += apr_cert_list[i].raw.size + 3; + /* hold size + * for uint24 */ + } + + /* if no certificates were found then send: + * 0B 00 00 03 00 00 00 // Certificate with no certs + * instead of: + * 0B 00 00 00 // empty certificate handshake + * + * ( the above is the whole handshake message, not + * the one produced here ) + */ + + (*data) = gnutls_malloc (ret); + pdata = (*data); + + if (pdata == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + _gnutls_write_uint24 (ret - 3, pdata); + pdata += 3; + for (i = 0; i < apr_cert_list_length; i++) + { + _gnutls_write_datum24 (pdata, apr_cert_list[i].raw); + pdata += (3 + apr_cert_list[i].raw.size); + } + + return ret; +} + +enum PGPKeyDescriptorType +{ PGP_KEY_FINGERPRINT, PGP_KEY }; + +int +_gnutls_gen_openpgp_certificate (gnutls_session_t session, opaque ** data) +{ + int ret; + opaque *pdata; + gnutls_cert *apr_cert_list; + gnutls_privkey *apr_pkey; + int apr_cert_list_length; + + /* find the appropriate certificate */ + if ((ret = + _gnutls_get_selected_cert (session, &apr_cert_list, + &apr_cert_list_length, &apr_pkey)) < 0) + { + gnutls_assert (); + return ret; + } + + ret = 3 + 1 + 3; + + if (apr_cert_list_length > 0) + ret += apr_cert_list[0].raw.size; + + (*data) = gnutls_malloc (ret); + pdata = (*data); + + if (pdata == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + _gnutls_write_uint24 (ret - 3, pdata); + pdata += 3; + + *pdata = PGP_KEY; /* whole key */ + pdata++; + + if (apr_cert_list_length > 0) + { + _gnutls_write_datum24 (pdata, apr_cert_list[0].raw); + pdata += (3 + apr_cert_list[0].raw.size); + } + else /* empty - no certificate */ + _gnutls_write_uint24 (0, pdata); + + return ret; +} + +int +_gnutls_gen_openpgp_certificate_fpr (gnutls_session_t session, opaque ** data) +{ + int ret, packet_size; + size_t fpr_size; + opaque *pdata; + gnutls_cert *apr_cert_list; + gnutls_privkey *apr_pkey; + int apr_cert_list_length; + + /* find the appropriate certificate */ + if ((ret = + _gnutls_get_selected_cert (session, &apr_cert_list, + &apr_cert_list_length, &apr_pkey)) < 0) + { + gnutls_assert (); + return ret; + } + + packet_size = 3 + 1; + + /* Only v4 fingerprints are sent + */ + if (apr_cert_list_length > 0 && apr_cert_list[0].version == 4) + packet_size += 20 + 1; + else /* empty certificate case */ + return _gnutls_gen_openpgp_certificate (session, data); + + (*data) = gnutls_malloc (packet_size); + pdata = (*data); + + if (pdata == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + _gnutls_write_uint24 (packet_size - 3, pdata); + pdata += 3; + + *pdata = PGP_KEY_FINGERPRINT; /* key fingerprint */ + pdata++; + + *pdata = 20; + pdata++; + + fpr_size = 20; + + if (_E_gnutls_openpgp_fingerprint == NULL) + { + gnutls_assert (); + return GNUTLS_E_INIT_LIBEXTRA; + } + + if ((ret = + _E_gnutls_openpgp_fingerprint (&apr_cert_list[0].raw, pdata, + &fpr_size)) < 0) + { + gnutls_assert (); + return ret; + } + + return packet_size; +} + + + +int +_gnutls_gen_cert_client_certificate (gnutls_session_t session, opaque ** data) +{ + switch (session->security_parameters.cert_type) + { + case GNUTLS_CRT_OPENPGP: + if (_gnutls_openpgp_send_fingerprint (session) == 0) + return _gnutls_gen_openpgp_certificate (session, data); + else + return _gnutls_gen_openpgp_certificate_fpr (session, data); + + case GNUTLS_CRT_X509: + return _gnutls_gen_x509_crt (session, data); + + default: + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } +} + +int +_gnutls_gen_cert_server_certificate (gnutls_session_t session, opaque ** data) +{ + switch (session->security_parameters.cert_type) + { + case GNUTLS_CRT_OPENPGP: + return _gnutls_gen_openpgp_certificate (session, data); + case GNUTLS_CRT_X509: + return _gnutls_gen_x509_crt (session, data); + default: + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } +} + +/* Process server certificate + */ + +#define CLEAR_CERTS for(x=0;xkey, GNUTLS_CRD_CERTIFICATE, NULL); + if (cred == NULL) + { + gnutls_assert (); + return GNUTLS_E_INSUFFICIENT_CREDENTIALS; + } + + + if ((ret = + _gnutls_auth_info_set (session, GNUTLS_CRD_CERTIFICATE, + sizeof (cert_auth_info_st), 1)) < 0) + { + gnutls_assert (); + return ret; + } + + info = _gnutls_get_auth_info (session); + + if (data == NULL || data_size == 0) + { + gnutls_assert (); + /* no certificate was sent */ + return GNUTLS_E_NO_CERTIFICATE_FOUND; + } + + DECR_LEN (dsize, 3); + size = _gnutls_read_uint24 (p); + p += 3; + + /* some implementations send 0B 00 00 06 00 00 03 00 00 00 + * instead of just 0B 00 00 03 00 00 00 as an empty certificate message. + */ + if (size == 0 || size == 3) + { + gnutls_assert (); + /* no certificate was sent */ + return GNUTLS_E_NO_CERTIFICATE_FOUND; + } + + i = dsize; + while (i > 0) + { + DECR_LEN (dsize, 3); + len = _gnutls_read_uint24 (p); + p += 3; + DECR_LEN (dsize, len); + peer_certificate_list_size++; + p += len; + i -= len + 3; + } + + if (peer_certificate_list_size == 0) + { + gnutls_assert (); + return GNUTLS_E_NO_CERTIFICATE_FOUND; + } + + /* Ok we now allocate the memory to hold the + * certificate list + */ + + peer_certificate_list = + gnutls_malloc (sizeof (gnutls_cert) * (peer_certificate_list_size)); + + if (peer_certificate_list == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + memset (peer_certificate_list, 0, sizeof (gnutls_cert) * + peer_certificate_list_size); + + p = data + 3; + + /* Now we start parsing the list (again). + * We don't use DECR_LEN since the list has + * been parsed before. + */ + + for (j = 0; j < peer_certificate_list_size; j++) + { + len = _gnutls_read_uint24 (p); + p += 3; + + tmp.size = len; + tmp.data = p; + + if ((ret = + _gnutls_x509_raw_cert_to_gcert (&peer_certificate_list + [j], &tmp, + CERT_ONLY_EXTENSIONS)) < 0) + { + gnutls_assert (); + goto cleanup; + } + + p += len; + } + + + if ((ret = + _gnutls_copy_certificate_auth_info (info, + peer_certificate_list, + peer_certificate_list_size)) < 0) + { + gnutls_assert (); + goto cleanup; + } + + if ((ret = + _gnutls_check_key_usage (&peer_certificate_list[0], + gnutls_kx_get (session))) < 0) + { + gnutls_assert (); + goto cleanup; + } + + ret = 0; + +cleanup: + CLEAR_CERTS; + gnutls_free (peer_certificate_list); + return ret; + +} + +#define CLEAR_CERTS for(x=0;xkey, GNUTLS_CRD_CERTIFICATE, NULL); + if (cred == NULL) + { + gnutls_assert (); + return GNUTLS_E_INSUFFICIENT_CREDENTIALS; + } + + if ((ret = + _gnutls_auth_info_set (session, GNUTLS_CRD_CERTIFICATE, + sizeof (cert_auth_info_st), 1)) < 0) + { + gnutls_assert (); + return ret; + } + + info = _gnutls_get_auth_info (session); + + if (data == NULL || data_size == 0) + { + gnutls_assert (); + return GNUTLS_E_NO_CERTIFICATE_FOUND; + } + + DECR_LEN (dsize, 3); + size = _gnutls_read_uint24 (p); + p += 3; + + if (size == 0) + { + gnutls_assert (); + /* no certificate was sent */ + return GNUTLS_E_NO_CERTIFICATE_FOUND; + } + i = dsize; + + /* Read PGPKeyDescriptor */ + DECR_LEN (dsize, 1); + if (*p == PGP_KEY_FINGERPRINT) + { /* the fingerprint */ + p++; + + DECR_LEN (dsize, 1); + len = (uint8_t) * p; + p++; + + if (len != 20) + { + gnutls_assert (); + return GNUTLS_E_OPENPGP_FINGERPRINT_UNSUPPORTED; + } + + DECR_LEN (dsize, 20); + + /* request the actual key from our database, or + * a key server or anything. + */ + if (_E_gnutls_openpgp_request_key == NULL) + { + gnutls_assert (); + return GNUTLS_E_INIT_LIBEXTRA; + } + if ((ret = + _E_gnutls_openpgp_request_key (session, &akey, cred, p, 20)) < 0) + { + gnutls_assert (); + return ret; + } + tmp = akey; + peer_certificate_list_size++; + + } + else if (*p == PGP_KEY) + { /* the whole key */ + + p++; + + /* Read the actual certificate */ + DECR_LEN (dsize, 3); + len = _gnutls_read_uint24 (p); + p += 3; + + if (len == 0) + { + gnutls_assert (); + /* no certificate was sent */ + return GNUTLS_E_NO_CERTIFICATE_FOUND; + } + + DECR_LEN (dsize, len); + peer_certificate_list_size++; + + tmp.size = len; + tmp.data = p; + + } + else + { + gnutls_assert (); + return GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE; + } + + /* ok we now have the peer's key in tmp datum + */ + + if (peer_certificate_list_size == 0) + { + gnutls_assert (); + return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; + } + + peer_certificate_list = + gnutls_alloca (sizeof (gnutls_cert) * (peer_certificate_list_size)); + if (peer_certificate_list == NULL) + { + gnutls_assert (); + ret = GNUTLS_E_MEMORY_ERROR; + goto cleanup; + } + memset (peer_certificate_list, 0, sizeof (gnutls_cert) * + peer_certificate_list_size); + + if (_E_gnutls_openpgp_raw_key_to_gcert == NULL) + { + gnutls_assert (); + ret = GNUTLS_E_INIT_LIBEXTRA; + goto cleanup; + } + + if ((ret = + _E_gnutls_openpgp_raw_key_to_gcert (&peer_certificate_list[0], + &tmp)) < 0) + { + gnutls_assert (); + goto cleanup; + } + + if ((ret = + _gnutls_copy_certificate_auth_info (info, + peer_certificate_list, + peer_certificate_list_size)) < 0) + { + gnutls_assert (); + goto cleanup; + } + + if ((ret = + _gnutls_check_key_usage (&peer_certificate_list[0], + gnutls_kx_get (session))) < 0) + { + gnutls_assert (); + goto cleanup; + } + + ret = 0; + +cleanup: + + _gnutls_free_datum (&akey); + CLEAR_CERTS; + gnutls_afree (peer_certificate_list); + return ret; + +} + +int +_gnutls_proc_cert_server_certificate (gnutls_session_t session, + opaque * data, size_t data_size) +{ + switch (session->security_parameters.cert_type) + { + case GNUTLS_CRT_OPENPGP: + return _gnutls_proc_openpgp_server_certificate (session, + data, data_size); + case GNUTLS_CRT_X509: + return _gnutls_proc_x509_server_certificate (session, data, data_size); + default: + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } +} + +#define MAX_SIGN_ALGOS 2 +typedef enum CertificateSigType +{ RSA_SIGN = 1, DSA_SIGN +} CertificateSigType; + +/* Checks if we support the given signature algorithm + * (RSA or DSA). Returns the corresponding gnutls_pk_algorithm_t + * if true; + */ +inline static int +_gnutls_check_supported_sign_algo (CertificateSigType algo) +{ + switch (algo) + { + case RSA_SIGN: + return GNUTLS_PK_RSA; + } + + return -1; +} + +int +_gnutls_proc_cert_cert_req (gnutls_session_t session, opaque * data, + size_t data_size) +{ + int size, ret; + opaque *p; + gnutls_certificate_credentials_t cred; + cert_auth_info_t info; + ssize_t dsize; + int i, j; + gnutls_pk_algorithm_t pk_algos[MAX_SIGN_ALGOS]; + int pk_algos_length; + gnutls_protocol_t ver = gnutls_protocol_get_version (session); + + cred = (gnutls_certificate_credentials_t) + _gnutls_get_cred (session->key, GNUTLS_CRD_CERTIFICATE, NULL); + if (cred == NULL) + { + gnutls_assert (); + return GNUTLS_E_INSUFFICIENT_CREDENTIALS; + } + + if ((ret = + _gnutls_auth_info_set (session, GNUTLS_CRD_CERTIFICATE, + sizeof (cert_auth_info_st), 0)) < 0) + { + gnutls_assert (); + return ret; + } + + info = _gnutls_get_auth_info (session); + + p = data; + dsize = data_size; + + DECR_LEN (dsize, 1); + size = p[0]; + p++; + /* check if the sign algorithm is supported. + */ + pk_algos_length = j = 0; + for (i = 0; i < size; i++, p++) + { + DECR_LEN (dsize, 1); + if ((ret = _gnutls_check_supported_sign_algo (*p)) > 0) + { + if (j < MAX_SIGN_ALGOS) + { + pk_algos[j++] = ret; + pk_algos_length++; + } + } + } + + if (pk_algos_length == 0) + { + gnutls_assert (); + return GNUTLS_E_UNKNOWN_PK_ALGORITHM; + } + + if (ver == GNUTLS_TLS1_2) + { + /* read supported hashes */ + int hash_num; + DECR_LEN (dsize, 1); + + hash_num = p[0] & 0xFF; + p++; + + DECR_LEN (dsize, hash_num); + p += hash_num; + } + + /* read the certificate authorities */ + DECR_LEN (dsize, 2); + size = _gnutls_read_uint16 (p); + p += 2; + + if (session->security_parameters.cert_type == GNUTLS_CRT_OPENPGP + && size != 0) + { + gnutls_assert (); // size should be zero + return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; + } + + DECR_LEN (dsize, size); + + /* now we ask the user to tell which one + * he wants to use. + */ + if ((ret = + _select_client_cert (session, p, size, pk_algos, pk_algos_length)) < 0) + { + gnutls_assert (); + return ret; + } + + /* We should reply with a certificate message, + * even if we have no certificate to send. + */ + session->key->certificate_requested = 1; + + return 0; +} + +int +_gnutls_gen_cert_client_cert_vrfy (gnutls_session_t session, opaque ** data) +{ + int ret; + gnutls_cert *apr_cert_list; + gnutls_privkey *apr_pkey; + int apr_cert_list_length, size; + gnutls_datum_t signature; + + *data = NULL; + + /* find the appropriate certificate */ + if ((ret = + _gnutls_get_selected_cert (session, &apr_cert_list, + &apr_cert_list_length, &apr_pkey)) < 0) + { + gnutls_assert (); + return ret; + } + + if (apr_cert_list_length > 0) + { + if ((ret = + _gnutls_tls_sign_hdata (session, + &apr_cert_list[0], + apr_pkey, &signature)) < 0) + { + gnutls_assert (); + return ret; + } + } + else + { + return 0; + } + + *data = gnutls_malloc (signature.size + 2); + if (*data == NULL) + { + _gnutls_free_datum (&signature); + return GNUTLS_E_MEMORY_ERROR; + } + size = signature.size; + _gnutls_write_uint16 (size, *data); + + memcpy (&(*data)[2], signature.data, size); + + _gnutls_free_datum (&signature); + + return size + 2; +} + +int +_gnutls_proc_cert_client_cert_vrfy (gnutls_session_t session, + opaque * data, size_t data_size) +{ + int size, ret; + ssize_t dsize = data_size; + opaque *pdata = data; + gnutls_datum_t sig; + cert_auth_info_t info = _gnutls_get_auth_info (session); + gnutls_cert peer_cert; + + if (info == NULL || info->ncerts == 0) + { + gnutls_assert (); + /* we need this in order to get peer's certificate */ + return GNUTLS_E_INTERNAL_ERROR; + } + + DECR_LEN (dsize, 2); + size = _gnutls_read_uint16 (pdata); + pdata += 2; + + DECR_LEN (dsize, size); + + sig.data = pdata; + sig.size = size; + + ret = _gnutls_raw_cert_to_gcert (&peer_cert, + session->security_parameters.cert_type, + &info->raw_certificate_list[0], + CERT_NO_COPY); + + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + if ((ret = _gnutls_verify_sig_hdata (session, &peer_cert, &sig)) < 0) + { + gnutls_assert (); + _gnutls_gcert_deinit (&peer_cert); + return ret; + } + _gnutls_gcert_deinit (&peer_cert); + + return 0; +} + +#define CERTTYPE_SIZE 3 +int +_gnutls_gen_cert_server_cert_req (gnutls_session_t session, opaque ** data) +{ + gnutls_certificate_credentials_t cred; + int size; + opaque *pdata; + gnutls_protocol_t ver = gnutls_protocol_get_version (session); + + /* Now we need to generate the RDN sequence. This is + * already in the CERTIFICATE_CRED structure, to improve + * performance. + */ + + cred = (gnutls_certificate_credentials_t) + _gnutls_get_cred (session->key, GNUTLS_CRD_CERTIFICATE, NULL); + if (cred == NULL) + { + gnutls_assert (); + return GNUTLS_E_INSUFFICIENT_CREDENTIALS; + } + + size = CERTTYPE_SIZE + 2; /* 2 for gnutls_certificate_type_t + 2 for size of rdn_seq + */ + + if (session->security_parameters.cert_type == GNUTLS_CRT_X509 && + session->internals.ignore_rdn_sequence == 0) + size += cred->x509_rdn_sequence.size; + + if (ver == GNUTLS_TLS1_2) + /* Need at least one byte to announce the number of supported hash + functions (see below). */ + size += 1; + + (*data) = gnutls_malloc (size); + pdata = (*data); + + if (pdata == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + pdata[0] = CERTTYPE_SIZE - 1; + + pdata[1] = RSA_SIGN; + pdata[2] = DSA_SIGN; /* only these for now */ + pdata += CERTTYPE_SIZE; + + if (ver == GNUTLS_TLS1_2) + { + /* Supported hashes (nothing for now -- FIXME). */ + *pdata = 0; + pdata++; + } + + if (session->security_parameters.cert_type == GNUTLS_CRT_X509 && + session->internals.ignore_rdn_sequence == 0) + { + _gnutls_write_datum16 (pdata, cred->x509_rdn_sequence); + /* pdata += cred->x509_rdn_sequence.size + 2; */ + } + else + { + _gnutls_write_uint16 (0, pdata); + /* pdata+=2; */ + } + + return size; +} + + +/* This function will return the appropriate certificate to use. + * Fills in the apr_cert_list, apr_cert_list_length and apr_pkey. + * The return value is a negative value on error. + * + * It is normal to return 0 with no certificates in client side. + * + */ +int +_gnutls_get_selected_cert (gnutls_session_t session, + gnutls_cert ** apr_cert_list, + int *apr_cert_list_length, + gnutls_privkey ** apr_pkey) +{ + if (session->security_parameters.entity == GNUTLS_SERVER) + { + + /* select_client_cert() has been called before. + */ + + *apr_cert_list = session->internals.selected_cert_list; + *apr_pkey = session->internals.selected_key; + *apr_cert_list_length = session->internals.selected_cert_list_length; + + if (*apr_cert_list_length == 0 || *apr_cert_list == NULL) + { + gnutls_assert (); + return GNUTLS_E_INSUFFICIENT_CREDENTIALS; + } + + } + else + { /* CLIENT SIDE + */ + + /* we have already decided which certificate + * to send. + */ + *apr_cert_list = session->internals.selected_cert_list; + *apr_cert_list_length = session->internals.selected_cert_list_length; + *apr_pkey = session->internals.selected_key; + + } + + return 0; +} + +/* converts the given x509 certificate to gnutls_cert* and allocates + * space for them. + */ +static gnutls_cert * +alloc_and_load_x509_certs (gnutls_x509_crt_t * certs, unsigned ncerts) +{ + gnutls_cert *local_certs; + int ret = 0; + unsigned i, j; + + if (certs == NULL) + return NULL; + + local_certs = gnutls_malloc (sizeof (gnutls_cert) * ncerts); + if (local_certs == NULL) + { + gnutls_assert (); + return NULL; + } + + for (i = 0; i < ncerts; i++) + { + ret = _gnutls_x509_crt_to_gcert (&local_certs[i], certs[i], 0); + if (ret < 0) + break; + } + + if (ret < 0) + { + gnutls_assert (); + for (j = 0; j < i; j++) + { + _gnutls_gcert_deinit (&local_certs[j]); + } + gnutls_free (local_certs); + return NULL; + } + + return local_certs; +} + +/* converts the given x509 key to gnutls_privkey* and allocates + * space for it. + */ +static gnutls_privkey * +alloc_and_load_x509_key (gnutls_x509_privkey_t key) +{ + gnutls_privkey *local_key; + int ret = 0; + + if (key == NULL) + return NULL; + + local_key = gnutls_malloc (sizeof (gnutls_privkey)); + if (local_key == NULL) + { + gnutls_assert (); + return NULL; + } + + ret = _gnutls_x509_privkey_to_gkey (local_key, key); + if (ret < 0) + { + gnutls_assert (); + return NULL; + } + + return local_key; +} + +/* converts the given pgp certificate to gnutls_cert* and allocates + * space for them. + */ +static gnutls_cert * +alloc_and_load_pgp_certs (gnutls_openpgp_crt_t cert) +{ + gnutls_cert *local_certs; + int ret = 0; + + if (cert == NULL) + return NULL; + + local_certs = gnutls_malloc (sizeof (gnutls_cert)); + if (local_certs == NULL) + { + gnutls_assert (); + return NULL; + } + + if (_E_gnutls_openpgp_crt_to_gcert == NULL) + { + gnutls_assert (); + return NULL; + } + + ret = _E_gnutls_openpgp_crt_to_gcert (local_certs, cert); + if (ret < 0) + { + gnutls_assert (); + return NULL; + } + + if (ret < 0) + { + gnutls_assert (); + _gnutls_gcert_deinit (local_certs); + gnutls_free (local_certs); + return NULL; + } + + return local_certs; +} + +/* converts the given raw key to gnutls_privkey* and allocates + * space for it. + */ +static gnutls_privkey * +alloc_and_load_pgp_key (const gnutls_openpgp_privkey_t key) +{ + gnutls_privkey *local_key; + int ret = 0; + + if (key == NULL) + return NULL; + + local_key = gnutls_malloc (sizeof (gnutls_privkey)); + if (local_key == NULL) + { + gnutls_assert (); + return NULL; + } + + if (_E_gnutls_openpgp_privkey_to_gkey == NULL) + { + gnutls_assert (); + return NULL; + } + + ret = _E_gnutls_openpgp_privkey_to_gkey (local_key, key); + if (ret < 0) + { + gnutls_assert (); + return NULL; + } + + return local_key; +} + + +void +_gnutls_selected_certs_deinit (gnutls_session_t session) +{ + if (session->internals.selected_need_free != 0) + { + int i; + + for (i = 0; i < session->internals.selected_cert_list_length; i++) + { + _gnutls_gcert_deinit (&session->internals.selected_cert_list[i]); + } + gnutls_free (session->internals.selected_cert_list); + session->internals.selected_cert_list = NULL; + session->internals.selected_cert_list_length = 0; + + _gnutls_gkey_deinit (session->internals.selected_key); + if (session->internals.selected_key) + { + gnutls_free (session->internals.selected_key); + session->internals.selected_key = NULL; + } + } + + return; +} + +void +_gnutls_selected_certs_set (gnutls_session_t session, + gnutls_cert * certs, int ncerts, + gnutls_privkey * key, int need_free) +{ + _gnutls_selected_certs_deinit (session); + + session->internals.selected_cert_list = certs; + session->internals.selected_cert_list_length = ncerts; + session->internals.selected_key = key; + session->internals.selected_need_free = need_free; + +} + + +/* finds the most appropriate certificate in the cert list. + * The 'appropriate' is defined by the user. + * + * requested_algo holds the parameters required by the peer (RSA, DSA + * or -1 for any). + * + * Returns 0 on success and a negative value on error. The + * selected certificate will be in session->internals.selected_*. + * + */ +int +_gnutls_server_select_cert (gnutls_session_t session, + gnutls_pk_algorithm_t requested_algo) +{ + unsigned i; + int idx, ret; + gnutls_certificate_credentials_t cred; + + cred = (gnutls_certificate_credentials_t) + _gnutls_get_cred (session->key, GNUTLS_CRD_CERTIFICATE, NULL); + if (cred == NULL) + { + gnutls_assert (); + return GNUTLS_E_INSUFFICIENT_CREDENTIALS; + } + + /* If the callback which retrieves certificate has been set, + * use it and leave. + */ + if (cred->server_get_cert_callback != NULL) + return call_get_cert_callback (session, NULL, 0, NULL, 0); + + /* Otherwise... */ + + ret = 0; + idx = -1; /* default is use no certificate */ + + + for (i = 0; i < cred->ncerts; i++) + { + /* find one compatible certificate + */ + if (requested_algo == GNUTLS_PK_ANY || + requested_algo == cred->cert_list[i][0].subject_pk_algorithm) + { + /* if cert type matches + */ + if (session->security_parameters.cert_type == + cred->cert_list[i][0].cert_type) + { + idx = i; + break; + } + } + } + + /* store the certificate pointer for future use, in the handshake. + * (This will allow not calling this callback again.) + */ + if (idx >= 0 && ret == 0) + { + _gnutls_selected_certs_set (session, + &cred->cert_list[idx][0], + cred->cert_list_length[idx], + &cred->pkey[idx], 0); + } + else + /* Certificate does not support REQUESTED_ALGO. */ + ret = GNUTLS_E_INSUFFICIENT_CREDENTIALS; + + return ret; +} + +/* Frees the rsa_info_st structure. + */ +void +_gnutls_free_rsa_info (rsa_info_st * rsa) +{ + _gnutls_free_datum (&rsa->modulus); + _gnutls_free_datum (&rsa->exponent); +} diff --git a/src/daemon/https/tls/auth_cert.h b/src/daemon/https/tls/auth_cert.h new file mode 100644 index 00000000..86e0230f --- /dev/null +++ b/src/daemon/https/tls/auth_cert.h @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2002, 2003, 2004, 2005, 2007 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +#ifndef AUTH_CERT_H +# define AUTH_CERT_H + +# include "gnutls_cert.h" +# include "gnutls_auth.h" +# include "auth_dh_common.h" +# include "x509.h" +# include "openpgp.h" + +/* This structure may be complex, but it's the only way to + * support a server that has multiple certificates + */ +typedef struct gnutls_certificate_credentials_st +{ + gnutls_dh_params_t dh_params; + gnutls_rsa_params_t rsa_params; + /* this callback is used to retrieve the DH or RSA + * parameters. + */ + gnutls_params_function *params_func; + + gnutls_cert **cert_list; + /* contains a list of a list of certificates. + * eg (X509): [0] certificate1, certificate11, certificate111 + * (if more than one, one certificate certifies the one before) + * [1] certificate2, certificate22, ... + */ + unsigned *cert_list_length; + /* contains the number of the certificates in a + * row (should be 1 for OpenPGP keys). + */ + unsigned ncerts; /* contains the number of columns in cert_list. + * This is the same with the number of pkeys. + */ + + gnutls_privkey *pkey; + /* private keys. It contains ncerts private + * keys. pkey[i] corresponds to certificate in + * cert_list[i][0]. + */ + + /* OpenPGP specific stuff */ + +#ifndef KEYRING_HACK + gnutls_openpgp_keyring_t keyring; +#else + gnutls_datum_t keyring; + int keyring_format; +#endif + + /* X509 specific stuff */ + + gnutls_x509_crt_t *x509_ca_list; + unsigned x509_ncas; /* number of CAs in the ca_list + */ + + gnutls_x509_crl_t *x509_crl_list; + unsigned x509_ncrls; /* number of CRLs in the crl_list + */ + + unsigned int verify_flags; /* flags to be used at + * certificate verification. + */ + unsigned int verify_depth; + unsigned int verify_bits; + + /* holds a sequence of the + * RDNs of the CAs above. + * This is better than + * generating on every handshake. + */ + gnutls_datum_t x509_rdn_sequence; + + gnutls_certificate_client_retrieve_function *client_get_cert_callback; + gnutls_certificate_server_retrieve_function *server_get_cert_callback; +} certificate_credentials_st; + +typedef struct rsa_info_st +{ + gnutls_datum_t modulus; + gnutls_datum_t exponent; +} rsa_info_st; + +typedef struct cert_auth_info_st +{ + int certificate_requested; /* if the peer requested certificate + * this is non zero; + */ + + /* These (dh/rsa) are just copies from the credentials_t structure. + * They must be freed. + */ + dh_info_st dh; + rsa_info_st rsa_export; + + gnutls_datum_t *raw_certificate_list; /* holds the raw certificate of the + * peer. + */ + unsigned int ncerts; /* holds the size of the list above */ +} *cert_auth_info_t; + +typedef struct cert_auth_info_st cert_auth_info_st; + +void _gnutls_free_rsa_info (rsa_info_st * rsa); + +/* AUTH X509 functions */ +int _gnutls_gen_cert_server_certificate (gnutls_session_t, opaque **); +int _gnutls_gen_cert_client_certificate (gnutls_session_t, opaque **); +int _gnutls_gen_cert_client_cert_vrfy (gnutls_session_t, opaque **); +int _gnutls_gen_cert_server_cert_req (gnutls_session_t, opaque **); +int _gnutls_proc_cert_cert_req (gnutls_session_t, opaque *, size_t); +int _gnutls_proc_cert_client_cert_vrfy (gnutls_session_t, opaque *, size_t); +int _gnutls_proc_cert_server_certificate (gnutls_session_t, opaque *, size_t); +int _gnutls_get_selected_cert (gnutls_session_t session, + gnutls_cert ** apr_cert_list, + int *apr_cert_list_length, + gnutls_privkey ** apr_pkey); + +int _gnutls_server_select_cert (struct gnutls_session_int *, + gnutls_pk_algorithm_t); +void _gnutls_selected_certs_deinit (gnutls_session_t session); +void _gnutls_selected_certs_set (gnutls_session_t session, + gnutls_cert * certs, int ncerts, + gnutls_privkey * key, int need_free); + +#define _gnutls_proc_cert_client_certificate _gnutls_proc_cert_server_certificate + +gnutls_rsa_params_t _gnutls_certificate_get_rsa_params (gnutls_rsa_params_t + rsa_params, + gnutls_params_function + * func, + gnutls_session_t); + +#endif diff --git a/src/daemon/https/tls/auth_dh_common.c b/src/daemon/https/tls/auth_dh_common.c new file mode 100644 index 00000000..f1b82bf9 --- /dev/null +++ b/src/daemon/https/tls/auth_dh_common.c @@ -0,0 +1,369 @@ +/* + * Copyright (C) 2002, 2003, 2004, 2005, 2007 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +/* This file contains common stuff in Ephemeral Diffie Hellman (DHE) and + * Anonymous DH key exchange(DHA). These are used in the handshake procedure + * of the certificate and anoymous authentication. + */ + +#include "gnutls_int.h" +#include "gnutls_auth_int.h" +#include "gnutls_errors.h" +#include "gnutls_dh.h" +#include "gnutls_num.h" +#include "gnutls_sig.h" +#include +#include +#include +#include +#include + +/* Frees the dh_info_st structure. + */ +void +_gnutls_free_dh_info (dh_info_st * dh) +{ + dh->secret_bits = 0; + _gnutls_free_datum (&dh->prime); + _gnutls_free_datum (&dh->generator); + _gnutls_free_datum (&dh->public_key); +} + +int +_gnutls_proc_dh_common_client_kx (gnutls_session_t session, + opaque * data, size_t _data_size, + mpi_t g, mpi_t p) +{ + uint16_t n_Y; + size_t _n_Y; + int ret; + ssize_t data_size = _data_size; + + + DECR_LEN (data_size, 2); + n_Y = _gnutls_read_uint16 (&data[0]); + _n_Y = n_Y; + + DECR_LEN (data_size, n_Y); + if (_gnutls_mpi_scan_nz (&session->key->client_Y, &data[2], &_n_Y)) + { + gnutls_assert (); + return GNUTLS_E_MPI_SCAN_FAILED; + } + + _gnutls_dh_set_peer_public (session, session->key->client_Y); + + session->key->KEY = + gnutls_calc_dh_key (session->key->client_Y, session->key->dh_secret, p); + + if (session->key->KEY == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + _gnutls_mpi_release (&session->key->client_Y); + _gnutls_mpi_release (&session->key->dh_secret); + + + if (_gnutls_cipher_suite_get_kx_algo + (&session->security_parameters.current_cipher_suite) + != GNUTLS_KX_DHE_PSK) + { + ret = _gnutls_mpi_dprint (&session->key->key, session->key->KEY); + } + /* In DHE_PSK the key is set differently + else + { + gnutls_datum_t tmp_dh_key; + ret = _gnutls_mpi_dprint (&tmp_dh_key, session->key->KEY); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + ret = _gnutls_set_psk_session_key (session, &tmp_dh_key); + _gnutls_free_datum (&tmp_dh_key); + + } + */ + + _gnutls_mpi_release (&session->key->KEY); + + if (ret < 0) + { + return ret; + } + + return 0; +} + +int +_gnutls_gen_dh_common_client_kx (gnutls_session_t session, opaque ** data) +{ + mpi_t x = NULL, X = NULL; + size_t n_X; + int ret; + + *data = NULL; + + X = gnutls_calc_dh_secret (&x, session->key->client_g, + session->key->client_p); + if (X == NULL || x == NULL) + { + gnutls_assert (); + ret = GNUTLS_E_MEMORY_ERROR; + goto error; + } + + _gnutls_dh_set_secret_bits (session, _gnutls_mpi_get_nbits (x)); + + _gnutls_mpi_print (NULL, &n_X, X); + (*data) = gnutls_malloc (n_X + 2); + if (*data == NULL) + { + ret = GNUTLS_E_MEMORY_ERROR; + goto error; + } + + _gnutls_mpi_print (&(*data)[2], &n_X, X); + _gnutls_mpi_release (&X); + + _gnutls_write_uint16 (n_X, &(*data)[0]); + + /* calculate the key after calculating the message */ + session->key->KEY = + gnutls_calc_dh_key (session->key->client_Y, x, session->key->client_p); + + _gnutls_mpi_release (&x); + if (session->key->KEY == NULL) + { + gnutls_assert (); + ret = GNUTLS_E_MEMORY_ERROR; + goto error; + } + + /* THESE SHOULD BE DISCARDED */ + _gnutls_mpi_release (&session->key->client_Y); + _gnutls_mpi_release (&session->key->client_p); + _gnutls_mpi_release (&session->key->client_g); + + if (_gnutls_cipher_suite_get_kx_algo + (&session->security_parameters.current_cipher_suite) + != GNUTLS_KX_DHE_PSK) + { + ret = _gnutls_mpi_dprint (&session->key->key, session->key->KEY); + } + /* In DHE_PSK the key is set differently + else + { + gnutls_datum_t tmp_dh_key; + ret = _gnutls_mpi_dprint (&tmp_dh_key, session->key->KEY); + if (ret < 0) + { + gnutls_assert (); + goto error; + } + + ret = _gnutls_set_psk_session_key (session, &tmp_dh_key); + _gnutls_free_datum (&tmp_dh_key); + + }*/ + + _gnutls_mpi_release (&session->key->KEY); + + if (ret < 0) + { + gnutls_assert (); + goto error; + } + + return n_X + 2; + +error: + _gnutls_mpi_release (&x); + _gnutls_mpi_release (&X); + gnutls_free (*data); + *data = NULL; + return ret; +} + +int +_gnutls_proc_dh_common_server_kx (gnutls_session_t session, + opaque * data, size_t _data_size, int psk) +{ + uint16_t n_Y, n_g, n_p; + size_t _n_Y, _n_g, _n_p; + uint8_t *data_p; + uint8_t *data_g; + uint8_t *data_Y; + int i, bits, psk_size, ret; + ssize_t data_size = _data_size; + + i = 0; + + if (psk != 0) + { + DECR_LEN (data_size, 2); + psk_size = _gnutls_read_uint16 (&data[i]); + DECR_LEN (data_size, psk_size); + i += 2 + psk_size; + } + + DECR_LEN (data_size, 2); + n_p = _gnutls_read_uint16 (&data[i]); + i += 2; + + DECR_LEN (data_size, n_p); + data_p = &data[i]; + i += n_p; + + DECR_LEN (data_size, 2); + n_g = _gnutls_read_uint16 (&data[i]); + i += 2; + + DECR_LEN (data_size, n_g); + data_g = &data[i]; + i += n_g; + + DECR_LEN (data_size, 2); + n_Y = _gnutls_read_uint16 (&data[i]); + i += 2; + + DECR_LEN (data_size, n_Y); + data_Y = &data[i]; + i += n_Y; + + _n_Y = n_Y; + _n_g = n_g; + _n_p = n_p; + + if (_gnutls_mpi_scan_nz (&session->key->client_Y, data_Y, &_n_Y) != 0) + { + gnutls_assert (); + return GNUTLS_E_MPI_SCAN_FAILED; + } + + if (_gnutls_mpi_scan_nz (&session->key->client_g, data_g, &_n_g) != 0) + { + gnutls_assert (); + return GNUTLS_E_MPI_SCAN_FAILED; + } + if (_gnutls_mpi_scan_nz (&session->key->client_p, data_p, &_n_p) != 0) + { + gnutls_assert (); + return GNUTLS_E_MPI_SCAN_FAILED; + } + + bits = _gnutls_dh_get_allowed_prime_bits (session); + if (bits < 0) + { + gnutls_assert (); + return bits; + } + + if (_gnutls_mpi_get_nbits (session->key->client_p) < (size_t) bits) + { + /* the prime used by the peer is not acceptable + */ + gnutls_assert (); + return GNUTLS_E_DH_PRIME_UNACCEPTABLE; + } + + _gnutls_dh_set_group (session, session->key->client_g, + session->key->client_p); + _gnutls_dh_set_peer_public (session, session->key->client_Y); + + ret = n_Y + n_p + n_g + 6; + if (psk != 0) + ret += 2; + + return ret; +} + +/* If the psk flag is set, then an empty psk_identity_hint will + * be inserted */ +int +_gnutls_dh_common_print_server_kx (gnutls_session_t session, + mpi_t g, mpi_t p, opaque ** data, int psk) +{ + mpi_t x, X; + size_t n_X, n_g, n_p; + int ret, data_size, pos; + uint8_t *pdata; + + X = gnutls_calc_dh_secret (&x, g, p); + if (X == NULL || x == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + session->key->dh_secret = x; + _gnutls_dh_set_secret_bits (session, _gnutls_mpi_get_nbits (x)); + + _gnutls_mpi_print (NULL, &n_g, g); + _gnutls_mpi_print (NULL, &n_p, p); + _gnutls_mpi_print (NULL, &n_X, X); + + data_size = n_g + n_p + n_X + 6; + if (psk != 0) + data_size += 2; + + (*data) = gnutls_malloc (data_size); + if (*data == NULL) + { + _gnutls_mpi_release (&X); + return GNUTLS_E_MEMORY_ERROR; + } + + pos = 0; + pdata = *data; + + if (psk != 0) + { + _gnutls_write_uint16 (0, &pdata[pos]); + pos += 2; + } + + _gnutls_mpi_print (&pdata[pos + 2], &n_p, p); + _gnutls_write_uint16 (n_p, &pdata[pos]); + + pos += n_p + 2; + + _gnutls_mpi_print (&pdata[pos + 2], &n_g, g); + _gnutls_write_uint16 (n_g, &pdata[pos]); + + pos += n_g + 2; + + _gnutls_mpi_print (&pdata[pos + 2], &n_X, X); + _gnutls_mpi_release (&X); + + _gnutls_write_uint16 (n_X, &pdata[pos]); + + ret = data_size; + + return ret; +} diff --git a/src/daemon/https/tls/auth_dh_common.h b/src/daemon/https/tls/auth_dh_common.h new file mode 100644 index 00000000..be0ad066 --- /dev/null +++ b/src/daemon/https/tls/auth_dh_common.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +#ifndef AUTH_DH_COMMON +# define AUTH_DH_COMMON + +typedef struct +{ + int secret_bits; + + gnutls_datum_t prime; + gnutls_datum_t generator; + gnutls_datum_t public_key; +} dh_info_st; + +void _gnutls_free_dh_info (dh_info_st * dh); +int _gnutls_gen_dh_common_client_kx (gnutls_session_t, opaque **); +int _gnutls_proc_dh_common_client_kx (gnutls_session_t session, + opaque * data, size_t _data_size, + mpi_t p, mpi_t g); +int _gnutls_dh_common_print_server_kx (gnutls_session_t, mpi_t g, mpi_t p, + opaque ** data, int psk); +int _gnutls_proc_dh_common_server_kx (gnutls_session_t session, + opaque * data, size_t _data_size, + int psk); + +#endif diff --git a/src/daemon/https/tls/auth_dhe.c b/src/daemon/https/tls/auth_dhe.c new file mode 100644 index 00000000..0262bda3 --- /dev/null +++ b/src/daemon/https/tls/auth_dhe.c @@ -0,0 +1,276 @@ +/* + * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2007 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +/* This file contains everything for the Ephemeral Diffie Hellman (DHE) + * key exchange. This is used in the handshake procedure of the certificate + * authentication. + */ + +#include "gnutls_int.h" +#include "gnutls_auth_int.h" +#include "gnutls_errors.h" +#include "gnutls_dh.h" +#include "gnutls_num.h" +#include "gnutls_sig.h" +#include +#include +#include +#include +#include + +static int gen_dhe_server_kx (gnutls_session_t, opaque **); +static int proc_dhe_server_kx (gnutls_session_t, opaque *, size_t); +static int proc_dhe_client_kx (gnutls_session_t, opaque *, size_t); + +const mod_auth_st dhe_rsa_auth_struct = { + "DHE_RSA", + _gnutls_gen_cert_server_certificate, + _gnutls_gen_cert_client_certificate, + gen_dhe_server_kx, + _gnutls_gen_dh_common_client_kx, + _gnutls_gen_cert_client_cert_vrfy, /* gen client cert vrfy */ + _gnutls_gen_cert_server_cert_req, /* server cert request */ + + _gnutls_proc_cert_server_certificate, + _gnutls_proc_cert_client_certificate, + proc_dhe_server_kx, + proc_dhe_client_kx, + _gnutls_proc_cert_client_cert_vrfy, /* proc client cert vrfy */ + _gnutls_proc_cert_cert_req /* proc server cert request */ +}; + +const mod_auth_st dhe_dss_auth_struct = { + "DHE_DSS", + _gnutls_gen_cert_server_certificate, + _gnutls_gen_cert_client_certificate, + gen_dhe_server_kx, + _gnutls_gen_dh_common_client_kx, + _gnutls_gen_cert_client_cert_vrfy, /* gen client cert vrfy */ + _gnutls_gen_cert_server_cert_req, /* server cert request */ + + _gnutls_proc_cert_server_certificate, + _gnutls_proc_cert_client_certificate, + proc_dhe_server_kx, + proc_dhe_client_kx, + _gnutls_proc_cert_client_cert_vrfy, /* proc client cert vrfy */ + _gnutls_proc_cert_cert_req /* proc server cert request */ +}; + + +static int +gen_dhe_server_kx (gnutls_session_t session, opaque ** data) +{ + mpi_t g, p; + const mpi_t *mpis; + int ret = 0, data_size; + int bits; + gnutls_cert *apr_cert_list; + gnutls_privkey *apr_pkey; + int apr_cert_list_length; + gnutls_datum_t signature, ddata; + gnutls_certificate_credentials_t cred; + gnutls_dh_params_t dh_params; + + cred = (gnutls_certificate_credentials_t) + _gnutls_get_cred (session->key, GNUTLS_CRD_CERTIFICATE, NULL); + if (cred == NULL) + { + gnutls_assert (); + return GNUTLS_E_INSUFFICIENT_CREDENTIALS; + } + + bits = _gnutls_dh_get_allowed_prime_bits (session); + + /* find the appropriate certificate */ + if ((ret = + _gnutls_get_selected_cert (session, &apr_cert_list, + &apr_cert_list_length, &apr_pkey)) < 0) + { + gnutls_assert (); + return ret; + } + + dh_params = + _gnutls_get_dh_params (cred->dh_params, cred->params_func, session); + mpis = _gnutls_dh_params_to_mpi (dh_params); + if (mpis == NULL) + { + gnutls_assert (); + return GNUTLS_E_NO_TEMPORARY_DH_PARAMS; + } + + p = mpis[0]; + g = mpis[1]; + + if ((ret = _gnutls_auth_info_set (session, GNUTLS_CRD_CERTIFICATE, + sizeof (cert_auth_info_st), 0)) < 0) + { + gnutls_assert (); + return ret; + } + + _gnutls_dh_set_group (session, g, p); + + ret = _gnutls_dh_common_print_server_kx (session, g, p, data, 0); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + data_size = ret; + + /* Generate the signature. */ + + ddata.data = *data; + ddata.size = data_size; + + if (apr_cert_list_length > 0) + { + if ((ret = + _gnutls_tls_sign_params (session, &apr_cert_list[0], + apr_pkey, &ddata, &signature)) < 0) + { + gnutls_assert (); + gnutls_free (*data); + return ret; + } + } + else + { + gnutls_assert (); + return data_size; /* do not put a signature - ILLEGAL! */ + } + + *data = gnutls_realloc_fast (*data, data_size + signature.size + 2); + if (*data == NULL) + { + _gnutls_free_datum (&signature); + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + _gnutls_write_datum16 (&(*data)[data_size], signature); + data_size += signature.size + 2; + + _gnutls_free_datum (&signature); + + return data_size; +} + +static int +proc_dhe_server_kx (gnutls_session_t session, opaque * data, + size_t _data_size) +{ + int sigsize; + gnutls_datum_t vparams, signature; + int ret; + cert_auth_info_t info = _gnutls_get_auth_info (session); + ssize_t data_size = _data_size; + gnutls_cert peer_cert; + + if (info == NULL || info->ncerts == 0) + { + gnutls_assert (); + /* we need this in order to get peer's certificate */ + return GNUTLS_E_INTERNAL_ERROR; + } + + ret = _gnutls_proc_dh_common_server_kx (session, data, _data_size, 0); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + /* VERIFY SIGNATURE */ + + vparams.size = ret; + vparams.data = data; + + DECR_LEN (data_size, 2); + sigsize = _gnutls_read_uint16 (&data[vparams.size]); + + DECR_LEN (data_size, sigsize); + signature.data = &data[vparams.size + 2]; + signature.size = sigsize; + + if ((ret = + _gnutls_raw_cert_to_gcert (&peer_cert, + session->security_parameters.cert_type, + &info->raw_certificate_list[0], + CERT_NO_COPY)) < 0) + { + gnutls_assert (); + return ret; + } + + ret = _gnutls_verify_sig_params (session, &peer_cert, &vparams, &signature); + + _gnutls_gcert_deinit (&peer_cert); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + return ret; +} + + + +static int +proc_dhe_client_kx (gnutls_session_t session, opaque * data, + size_t _data_size) +{ + gnutls_certificate_credentials_t cred; + int ret; + mpi_t p, g; + const mpi_t *mpis; + gnutls_dh_params_t dh_params; + + cred = (gnutls_certificate_credentials_t) + _gnutls_get_cred (session->key, GNUTLS_CRD_CERTIFICATE, NULL); + if (cred == NULL) + { + gnutls_assert (); + return GNUTLS_E_INSUFFICIENT_CREDENTIALS; + } + + dh_params = + _gnutls_get_dh_params (cred->dh_params, cred->params_func, session); + mpis = _gnutls_dh_params_to_mpi (dh_params); + if (mpis == NULL) + { + gnutls_assert (); + return GNUTLS_E_NO_TEMPORARY_DH_PARAMS; + } + + p = mpis[0]; + g = mpis[1]; + + ret = _gnutls_proc_dh_common_client_kx (session, data, _data_size, g, p); + + return ret; + +} diff --git a/src/daemon/https/tls/auth_rsa.c b/src/daemon/https/tls/auth_rsa.c new file mode 100644 index 00000000..4430009d --- /dev/null +++ b/src/daemon/https/tls/auth_rsa.c @@ -0,0 +1,408 @@ +/* + * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2007 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +/* This file contains the RSA key exchange part of the certificate + * authentication. + */ + +#include "gnutls_int.h" +#include "gnutls_auth_int.h" +#include "gnutls_errors.h" +#include "gnutls_dh.h" +#include "gnutls_num.h" +#include "libtasn1.h" +#include "gnutls_datum.h" +#include "auth_cert.h" +#include +#include +#include +#include "debug.h" +#include +#include +#include + +int _gnutls_gen_rsa_client_kx (gnutls_session_t, opaque **); +int _gnutls_proc_rsa_client_kx (gnutls_session_t, opaque *, size_t); + +const mod_auth_st rsa_auth_struct = { + "RSA", + _gnutls_gen_cert_server_certificate, + _gnutls_gen_cert_client_certificate, + NULL, /* gen server kx */ + _gnutls_gen_rsa_client_kx, + _gnutls_gen_cert_client_cert_vrfy, /* gen client cert vrfy */ + _gnutls_gen_cert_server_cert_req, /* server cert request */ + + _gnutls_proc_cert_server_certificate, + _gnutls_proc_cert_client_certificate, + NULL, /* proc server kx */ + _gnutls_proc_rsa_client_kx, /* proc client kx */ + _gnutls_proc_cert_client_cert_vrfy, /* proc client cert vrfy */ + _gnutls_proc_cert_cert_req /* proc server cert request */ +}; + +/* This function reads the RSA parameters from peer's certificate; + */ +int +_gnutls_get_public_rsa_params (gnutls_session_t session, + mpi_t params[MAX_PUBLIC_PARAMS_SIZE], + int *params_len) +{ + int ret; + cert_auth_info_t info; + gnutls_cert peer_cert; + int i; + + /* normal non export case */ + + info = _gnutls_get_auth_info (session); + + if (info == NULL || info->ncerts == 0) + { + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + ret = + _gnutls_raw_cert_to_gcert (&peer_cert, + session->security_parameters.cert_type, + &info->raw_certificate_list[0], + CERT_ONLY_PUBKEY | CERT_NO_COPY); + + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + + /* EXPORT case: */ + if (_gnutls_cipher_suite_get_kx_algo + (&session->security_parameters.current_cipher_suite) + == GNUTLS_KX_RSA_EXPORT + && _gnutls_mpi_get_nbits (peer_cert.params[0]) > 512) + { + + _gnutls_gcert_deinit (&peer_cert); + + if (session->key->rsa[0] == NULL || session->key->rsa[1] == NULL) + { + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + if (*params_len < 2) + { + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + *params_len = 2; + for (i = 0; i < *params_len; i++) + { + params[i] = _gnutls_mpi_copy (session->key->rsa[i]); + } + + return 0; + } + + /* end of export case */ + + if (*params_len < peer_cert.params_size) + { + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + *params_len = peer_cert.params_size; + + for (i = 0; i < *params_len; i++) + { + params[i] = _gnutls_mpi_copy (peer_cert.params[i]); + } + _gnutls_gcert_deinit (&peer_cert); + + return 0; +} + +/* This function reads the RSA parameters from the private key + */ +int +_gnutls_get_private_rsa_params (gnutls_session_t session, + mpi_t ** params, int *params_size) +{ + int bits; + gnutls_certificate_credentials_t cred; + gnutls_rsa_params_t rsa_params; + + cred = (gnutls_certificate_credentials_t) + _gnutls_get_cred (session->key, GNUTLS_CRD_CERTIFICATE, NULL); + if (cred == NULL) + { + gnutls_assert (); + return GNUTLS_E_INSUFFICIENT_CREDENTIALS; + } + + if (session->internals.selected_cert_list == NULL) + { + gnutls_assert (); + return GNUTLS_E_INSUFFICIENT_CREDENTIALS; + } + + bits = + _gnutls_mpi_get_nbits (session->internals.selected_cert_list[0]. + params[0]); + + if (_gnutls_cipher_suite_get_kx_algo + (&session->security_parameters.current_cipher_suite) + == GNUTLS_KX_RSA_EXPORT && bits > 512) + { + + rsa_params = + _gnutls_certificate_get_rsa_params (cred->rsa_params, + cred->params_func, session); + /* EXPORT case: */ + if (rsa_params == NULL) + { + gnutls_assert (); + return GNUTLS_E_NO_TEMPORARY_RSA_PARAMS; + } + + /* In the export case, we do use temporary RSA params + * of 512 bits size. The params in the certificate are + * used to sign this temporary stuff. + */ + *params_size = RSA_PRIVATE_PARAMS; + *params = rsa_params->params; + + return 0; + } + + /* non export cipher suites. */ + + *params_size = session->internals.selected_key->params_size; + *params = session->internals.selected_key->params; + + return 0; +} + +int +_gnutls_proc_rsa_client_kx (gnutls_session_t session, opaque * data, + size_t _data_size) +{ + gnutls_datum_t plaintext; + gnutls_datum_t ciphertext; + int ret, dsize; + mpi_t *params; + int params_len; + int randomize_key = 0; + ssize_t data_size = _data_size; + + if (gnutls_protocol_get_version (session) == GNUTLS_SSL3) + { + /* SSL 3.0 + */ + ciphertext.data = data; + ciphertext.size = data_size; + } + else + { + /* TLS 1.0 + */ + DECR_LEN (data_size, 2); + ciphertext.data = &data[2]; + dsize = _gnutls_read_uint16 (data); + + if (dsize != data_size) + { + gnutls_assert (); + return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; + } + ciphertext.size = dsize; + } + + ret = _gnutls_get_private_rsa_params (session, ¶ms, ¶ms_len); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + ret = _gnutls_pkcs1_rsa_decrypt (&plaintext, &ciphertext, params, params_len, 2); /* btype==2 */ + + if (ret < 0 || plaintext.size != TLS_MASTER_SIZE) + { + /* In case decryption fails then don't inform + * the peer. Just use a random key. (in order to avoid + * attack against pkcs-1 formating). + */ + gnutls_assert (); + _gnutls_x509_log ("auth_rsa: Possible PKCS #1 format attack\n"); + randomize_key = 1; + } + else + { + /* If the secret was properly formatted, then + * check the version number. + */ + if (_gnutls_get_adv_version_major (session) != plaintext.data[0] + || _gnutls_get_adv_version_minor (session) != plaintext.data[1]) + { + /* No error is returned here, if the version number check + * fails. We proceed normally. + * That is to defend against the attack described in the paper + * "Attacking RSA-based sessions in SSL/TLS" by Vlastimil Klima, + * Ondej Pokorny and Tomas Rosa. + */ + gnutls_assert (); + _gnutls_x509_log + ("auth_rsa: Possible PKCS #1 version check format attack\n"); + } + } + + if (randomize_key != 0) + { + session->key->key.size = TLS_MASTER_SIZE; + session->key->key.data = gnutls_malloc (session->key->key.size); + if (session->key->key.data == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + /* we do not need strong random numbers here. + */ + if (gc_nonce (session->key->key.data, session->key->key.size) != GC_OK) + { + gnutls_assert (); + return GNUTLS_E_RANDOM_FAILED; + } + + } + else + { + session->key->key.data = plaintext.data; + session->key->key.size = plaintext.size; + } + + /* This is here to avoid the version check attack + * discussed above. + */ + session->key->key.data[0] = _gnutls_get_adv_version_major (session); + session->key->key.data[1] = _gnutls_get_adv_version_minor (session); + + return 0; +} + + + +/* return RSA(random) using the peers public key + */ +int +_gnutls_gen_rsa_client_kx (gnutls_session_t session, opaque ** data) +{ + cert_auth_info_t auth = session->key->auth_info; + gnutls_datum_t sdata; /* data to send */ + mpi_t params[MAX_PUBLIC_PARAMS_SIZE]; + int params_len = MAX_PUBLIC_PARAMS_SIZE; + int ret, i; + gnutls_protocol_t ver; + + if (auth == NULL) + { + /* this shouldn't have happened. The proc_certificate + * function should have detected that. + */ + gnutls_assert (); + return GNUTLS_E_INSUFFICIENT_CREDENTIALS; + } + + session->key->key.size = TLS_MASTER_SIZE; + session->key->key.data = gnutls_secure_malloc (session->key->key.size); + + if (session->key->key.data == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + if (gc_pseudo_random (session->key->key.data, + session->key->key.size) != GC_OK) + { + gnutls_assert (); + return GNUTLS_E_RANDOM_FAILED; + } + + ver = _gnutls_get_adv_version (session); + + if (session->internals.rsa_pms_version[0] == 0) + { + session->key->key.data[0] = _gnutls_version_get_major (ver); + session->key->key.data[1] = _gnutls_version_get_minor (ver); + } + else + { /* use the version provided */ + session->key->key.data[0] = session->internals.rsa_pms_version[0]; + session->key->key.data[1] = session->internals.rsa_pms_version[1]; + } + + /* move RSA parameters to key (session). + */ + if ((ret = + _gnutls_get_public_rsa_params (session, params, ¶ms_len)) < 0) + { + gnutls_assert (); + return ret; + } + + if ((ret = + _gnutls_pkcs1_rsa_encrypt (&sdata, &session->key->key, + params, params_len, 2)) < 0) + { + gnutls_assert (); + return ret; + } + + for (i = 0; i < params_len; i++) + _gnutls_mpi_release (¶ms[i]); + + if (gnutls_protocol_get_version (session) == GNUTLS_SSL3) + { + /* SSL 3.0 */ + *data = sdata.data; + return sdata.size; + } + else + { /* TLS 1 */ + *data = gnutls_malloc (sdata.size + 2); + if (*data == NULL) + { + _gnutls_free_datum (&sdata); + return GNUTLS_E_MEMORY_ERROR; + } + _gnutls_write_datum16 (*data, sdata); + ret = sdata.size + 2; + _gnutls_free_datum (&sdata); + return ret; + } + +} diff --git a/src/daemon/https/tls/auth_rsa_export.c b/src/daemon/https/tls/auth_rsa_export.c new file mode 100644 index 00000000..3c84121d --- /dev/null +++ b/src/daemon/https/tls/auth_rsa_export.c @@ -0,0 +1,325 @@ +/* + * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2007 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +/* This file contains the RSA key exchange part of the certificate + * authentication. + */ + +#include "gnutls_int.h" +#include "gnutls_auth_int.h" +#include "gnutls_errors.h" +#include "gnutls_dh.h" +#include "gnutls_num.h" +#include "libtasn1.h" +#include "gnutls_datum.h" +#include "auth_cert.h" +#include +#include +#include +#include "debug.h" +#include +#include +#include +#include + +int _gnutls_gen_rsa_client_kx (gnutls_session_t, opaque **); +int _gnutls_proc_rsa_client_kx (gnutls_session_t, opaque *, size_t); +static int gen_rsa_export_server_kx (gnutls_session_t, opaque **); +static int proc_rsa_export_server_kx (gnutls_session_t, opaque *, size_t); + +const mod_auth_st rsa_export_auth_struct = { + "RSA EXPORT", + _gnutls_gen_cert_server_certificate, + _gnutls_gen_cert_client_certificate, + gen_rsa_export_server_kx, + _gnutls_gen_rsa_client_kx, + _gnutls_gen_cert_client_cert_vrfy, /* gen client cert vrfy */ + _gnutls_gen_cert_server_cert_req, /* server cert request */ + + _gnutls_proc_cert_server_certificate, + _gnutls_proc_cert_client_certificate, + proc_rsa_export_server_kx, + _gnutls_proc_rsa_client_kx, /* proc client kx */ + _gnutls_proc_cert_client_cert_vrfy, /* proc client cert vrfy */ + _gnutls_proc_cert_cert_req /* proc server cert request */ +}; + +static int +gen_rsa_export_server_kx (gnutls_session_t session, opaque ** data) +{ + gnutls_rsa_params_t rsa_params; + const mpi_t *rsa_mpis; + size_t n_e, n_m; + uint8_t *data_e, *data_m; + int ret = 0, data_size; + gnutls_cert *apr_cert_list; + gnutls_privkey *apr_pkey; + int apr_cert_list_length; + gnutls_datum_t signature, ddata; + cert_auth_info_t info; + gnutls_certificate_credentials_t cred; + + cred = (gnutls_certificate_credentials_t) + _gnutls_get_cred (session->key, GNUTLS_CRD_CERTIFICATE, NULL); + if (cred == NULL) + { + gnutls_assert (); + return GNUTLS_E_INSUFFICIENT_CREDENTIALS; + } + + /* find the appropriate certificate */ + if ((ret = + _gnutls_get_selected_cert (session, &apr_cert_list, + &apr_cert_list_length, &apr_pkey)) < 0) + { + gnutls_assert (); + return ret; + } + + /* abort sending this message if we have a certificate + * of 512 bits or less. + */ + if (apr_pkey && _gnutls_mpi_get_nbits (apr_pkey->params[0]) <= 512) + { + gnutls_assert (); + return GNUTLS_E_INT_RET_0; + } + + rsa_params = + _gnutls_certificate_get_rsa_params (cred->rsa_params, cred->params_func, + session); + rsa_mpis = _gnutls_rsa_params_to_mpi (rsa_params); + if (rsa_mpis == NULL) + { + gnutls_assert (); + return GNUTLS_E_NO_TEMPORARY_RSA_PARAMS; + } + + if ((ret = _gnutls_auth_info_set (session, GNUTLS_CRD_CERTIFICATE, + sizeof (cert_auth_info_st), 0)) < 0) + { + gnutls_assert (); + return ret; + } + + info = _gnutls_get_auth_info (session); + _gnutls_rsa_export_set_pubkey (session, rsa_mpis[1], rsa_mpis[0]); + + _gnutls_mpi_print (NULL, &n_m, rsa_mpis[0]); + _gnutls_mpi_print (NULL, &n_e, rsa_mpis[1]); + + (*data) = gnutls_malloc (n_e + n_m + 4); + if (*data == NULL) + { + return GNUTLS_E_MEMORY_ERROR; + } + + data_m = &(*data)[0]; + _gnutls_mpi_print (&data_m[2], &n_m, rsa_mpis[0]); + + _gnutls_write_uint16 (n_m, data_m); + + data_e = &data_m[2 + n_m]; + _gnutls_mpi_print (&data_e[2], &n_e, rsa_mpis[1]); + + _gnutls_write_uint16 (n_e, data_e); + + data_size = n_m + n_e + 4; + + + /* Generate the signature. */ + + ddata.data = *data; + ddata.size = data_size; + + if (apr_cert_list_length > 0) + { + if ((ret = + _gnutls_tls_sign_params (session, &apr_cert_list[0], + apr_pkey, &ddata, &signature)) < 0) + { + gnutls_assert (); + gnutls_free (*data); + *data = NULL; + return ret; + } + } + else + { + gnutls_assert (); + return data_size; /* do not put a signature - ILLEGAL! */ + } + + *data = gnutls_realloc_fast (*data, data_size + signature.size + 2); + if (*data == NULL) + { + _gnutls_free_datum (&signature); + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + _gnutls_write_datum16 (&((*data)[data_size]), signature); + data_size += signature.size + 2; + + _gnutls_free_datum (&signature); + + return data_size; +} + +/* if the peer's certificate is of 512 bits or less, returns non zero. + */ +int +_gnutls_peers_cert_less_512 (gnutls_session_t session) +{ + gnutls_cert peer_cert; + int ret; + cert_auth_info_t info = _gnutls_get_auth_info (session); + + if (info == NULL || info->ncerts == 0) + { + gnutls_assert (); + /* we need this in order to get peer's certificate */ + return 0; + } + + if ((ret = + _gnutls_raw_cert_to_gcert (&peer_cert, + session->security_parameters.cert_type, + &info->raw_certificate_list[0], + CERT_NO_COPY)) < 0) + { + gnutls_assert (); + return 0; + } + + if (peer_cert.subject_pk_algorithm != GNUTLS_PK_RSA) + { + gnutls_assert (); + _gnutls_gcert_deinit (&peer_cert); + return 0; + } + + if (_gnutls_mpi_get_nbits (peer_cert.params[0]) <= 512) + { + _gnutls_gcert_deinit (&peer_cert); + return 1; + } + + _gnutls_gcert_deinit (&peer_cert); + + return 0; +} + +static int +proc_rsa_export_server_kx (gnutls_session_t session, + opaque * data, size_t _data_size) +{ + uint16_t n_m, n_e; + size_t _n_m, _n_e; + uint8_t *data_m; + uint8_t *data_e; + int i, sigsize; + gnutls_datum_t vparams, signature; + int ret; + ssize_t data_size = _data_size; + cert_auth_info_t info; + gnutls_cert peer_cert; + + info = _gnutls_get_auth_info (session); + if (info == NULL || info->ncerts == 0) + { + gnutls_assert (); + /* we need this in order to get peer's certificate */ + return GNUTLS_E_INTERNAL_ERROR; + } + + + i = 0; + + DECR_LEN (data_size, 2); + n_m = _gnutls_read_uint16 (&data[i]); + i += 2; + + DECR_LEN (data_size, n_m); + data_m = &data[i]; + i += n_m; + + DECR_LEN (data_size, 2); + n_e = _gnutls_read_uint16 (&data[i]); + i += 2; + + DECR_LEN (data_size, n_e); + data_e = &data[i]; + i += n_e; + + _n_e = n_e; + _n_m = n_m; + + if (_gnutls_mpi_scan_nz (&session->key->rsa[0], data_m, &_n_m) != 0) + { + gnutls_assert (); + return GNUTLS_E_MPI_SCAN_FAILED; + } + + if (_gnutls_mpi_scan_nz (&session->key->rsa[1], data_e, &_n_e) != 0) + { + gnutls_assert (); + return GNUTLS_E_MPI_SCAN_FAILED; + } + + _gnutls_rsa_export_set_pubkey (session, session->key->rsa[1], + session->key->rsa[0]); + + /* VERIFY SIGNATURE */ + + vparams.size = n_m + n_e + 4; + vparams.data = data; + + DECR_LEN (data_size, 2); + sigsize = _gnutls_read_uint16 (&data[vparams.size]); + + DECR_LEN (data_size, sigsize); + signature.data = &data[vparams.size + 2]; + signature.size = sigsize; + + if ((ret = + _gnutls_raw_cert_to_gcert (&peer_cert, + session->security_parameters.cert_type, + &info->raw_certificate_list[0], + CERT_NO_COPY)) < 0) + { + gnutls_assert (); + return ret; + } + + ret = _gnutls_verify_sig_params (session, &peer_cert, &vparams, &signature); + + _gnutls_gcert_deinit (&peer_cert); + if (ret < 0) + { + gnutls_assert (); + } + + return ret; +} diff --git a/src/daemon/https/tls/debug.c b/src/daemon/https/tls/debug.c new file mode 100644 index 00000000..2ea31577 --- /dev/null +++ b/src/daemon/https/tls/debug.c @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +#include "gnutls_int.h" +#include "gnutls_errors.h" +#include +#include +#include + +#ifdef DEBUG + + +void +_gnutls_print_state (gnutls_session_t session) +{ + + _gnutls_debug_log ("GNUTLS State:\n"); + _gnutls_debug_log ("Connection End: %d\n", + session->security_parameters.entity); + _gnutls_debug_log ("Cipher Algorithm: %d\n", + session->security_parameters.read_bulk_cipher_algorithm); + _gnutls_debug_log ("MAC algorithm: %d\n", + session->security_parameters.read_mac_algorithm); + _gnutls_debug_log ("Compression Algorithm: %d\n", + session->security_parameters.read_compression_algorithm); + _gnutls_debug_log ("\n"); + +} + +#endif + +const char * +_gnutls_packet2str (content_type_t packet) +{ + switch (packet) + { + case GNUTLS_CHANGE_CIPHER_SPEC: + return "Change Cipher Spec"; + case GNUTLS_ALERT: + return "Alert"; + case GNUTLS_HANDSHAKE: + return "Handshake"; + case GNUTLS_APPLICATION_DATA: + return "Application Data"; + case GNUTLS_INNER_APPLICATION: + return "Inner Application"; + + default: + return "Unknown Packet"; + } +} + +const char * +_gnutls_handshake2str (gnutls_handshake_description_t handshake) +{ + + switch (handshake) + { + case GNUTLS_HANDSHAKE_HELLO_REQUEST: + return "HELLO REQUEST"; + break; + case GNUTLS_HANDSHAKE_CLIENT_HELLO: + return "CLIENT HELLO"; + break; + case GNUTLS_HANDSHAKE_SERVER_HELLO: + return "SERVER HELLO"; + break; + case GNUTLS_HANDSHAKE_CERTIFICATE_PKT: + return "CERTIFICATE"; + break; + case GNUTLS_HANDSHAKE_SERVER_KEY_EXCHANGE: + return "SERVER KEY EXCHANGE"; + break; + case GNUTLS_HANDSHAKE_CERTIFICATE_REQUEST: + return "CERTIFICATE REQUEST"; + break; + case GNUTLS_HANDSHAKE_SERVER_HELLO_DONE: + return "SERVER HELLO DONE"; + break; + case GNUTLS_HANDSHAKE_CERTIFICATE_VERIFY: + return "CERTIFICATE VERIFY"; + break; + case GNUTLS_HANDSHAKE_CLIENT_KEY_EXCHANGE: + return "CLIENT KEY EXCHANGE"; + break; + case GNUTLS_HANDSHAKE_FINISHED: + return "FINISHED"; + break; + case GNUTLS_HANDSHAKE_SUPPLEMENTAL: + return "SUPPLEMENTAL"; + break; + default: + return "Unknown Handshake packet"; + + } +} + +void +_gnutls_dump_mpi (const char *prefix, mpi_t a) +{ + opaque buf[1024]; + size_t n = sizeof buf; + + if (gcry_mpi_print (GCRYMPI_FMT_HEX, buf, n, &n, a)) + strcpy (buf, "[can't print value]"); /* Flawfinder: ignore */ + _gnutls_hard_log ("MPI: length: %d\n\t%s%s\n", (n - 1) / 2, prefix, buf); +} diff --git a/src/daemon/https/tls/debug.h b/src/daemon/https/tls/debug.h new file mode 100644 index 00000000..169bc21d --- /dev/null +++ b/src/daemon/https/tls/debug.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2000, 2001, 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +#ifdef DEBUG +void _gnutls_print_state (gnutls_session_t session); +#endif +const char *_gnutls_packet2str (content_type_t packet); +const char *_gnutls_handshake2str (gnutls_handshake_description_t handshake); +void _gnutls_dump_mpi (const char *prefix, mpi_t a); diff --git a/src/daemon/https/tls/defines.h b/src/daemon/https/tls/defines.h new file mode 100644 index 00000000..a53ce2e4 --- /dev/null +++ b/src/daemon/https/tls/defines.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +#ifndef DEFINES_H +# define DEFINES_H + +#if HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#ifdef NO_SSIZE_T +# define HAVE_SSIZE_T +typedef int ssize_t; +#endif + +#include +#include +#include +#include +#include + +// TODO check if these should go into config.h +#define SIZEOF_UNSIGNED_INT 4 +#define SIZEOF_UNSIGNED_LONG 8 +#define SIZEOF_UNSIGNED_LONG_INT SIZEOF_UNSIGNED_LONG + +/* some systems had problems with long long int, thus, + * it is not used. + */ +typedef struct +{ + unsigned char i[8]; +} uint64; + +#endif /* defines_h */ diff --git a/src/daemon/https/tls/ext_cert_type.c b/src/daemon/https/tls/ext_cert_type.c new file mode 100644 index 00000000..f970e1fe --- /dev/null +++ b/src/daemon/https/tls/ext_cert_type.c @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +/* This file contains the code the Certificate Type TLS extension. + * This extension is currently gnutls specific. + */ + +#include "gnutls_int.h" +#include "gnutls_errors.h" +#include "gnutls_num.h" +#include "ext_cert_type.h" +#include +#include + +inline static int _gnutls_num2cert_type (int num); +inline static int _gnutls_cert_type2num (int record_size); + +/* + * In case of a server: if a CERT_TYPE extension type is received then it stores + * into the session security parameters the new value. The server may use gnutls_session_certificate_type_get(), + * to access it. + * + * In case of a client: If a cert_types have been specified then we send the extension. + * + */ + +int +_gnutls_cert_type_recv_params (gnutls_session_t session, + const opaque * data, size_t _data_size) +{ + int new_type = -1, ret, i; + ssize_t data_size = _data_size; + + if (session->security_parameters.entity == GNUTLS_CLIENT) + { + if (data_size > 0) + { + if (data_size != 1) + { + gnutls_assert (); + return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; + } + + new_type = _gnutls_num2cert_type (data[0]); + + if (new_type < 0) + { + gnutls_assert (); + return new_type; + } + + /* Check if we support this cert_type */ + if ((ret = + _gnutls_session_cert_type_supported (session, new_type)) < 0) + { + gnutls_assert (); + return ret; + } + + _gnutls_session_cert_type_set (session, new_type); + } + } + else + { /* SERVER SIDE - we must check if the sent cert type is the right one + */ + if (data_size > 1) + { + uint8_t len; + + len = data[0]; + DECR_LEN (data_size, len); + + for (i = 0; i < len; i++) + { + new_type = _gnutls_num2cert_type (data[i + 1]); + + if (new_type < 0) + continue; + + /* Check if we support this cert_type */ + if ((ret = + _gnutls_session_cert_type_supported (session, + new_type)) < 0) + { + gnutls_assert (); + continue; + } + else + break; + /* new_type is ok */ + } + + if (new_type < 0) + { + gnutls_assert (); + return GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER; + } + + if ((ret = + _gnutls_session_cert_type_supported (session, new_type)) < 0) + { + gnutls_assert (); + /* The peer has requested unsupported certificate + * types. Instead of failing, procceed normally. + * (the ciphersuite selection would fail, or a + * non certificate ciphersuite will be selected). + */ + return 0; + } + + _gnutls_session_cert_type_set (session, new_type); + } + + + } + + return 0; +} + +/* returns data_size or a negative number on failure + */ +int +_gnutls_cert_type_send_params (gnutls_session_t session, opaque * data, + size_t data_size) +{ + unsigned len, i; + + /* this function sends the client extension data (dnsname) */ + if (session->security_parameters.entity == GNUTLS_CLIENT) + { + + if (session->internals.priorities.cert_type.algorithms > 0) + { + + len = session->internals.priorities.cert_type.algorithms; + + if (len == 1 && + session->internals.priorities.cert_type.priority[0] == + GNUTLS_CRT_X509) + { + /* We don't use this extension if X.509 certificates + * are used. + */ + return 0; + } + + if (data_size < len + 1) + { + gnutls_assert (); + return GNUTLS_E_SHORT_MEMORY_BUFFER; + } + + /* this is a vector! + */ + data[0] = (uint8_t) len; + + for (i = 0; i < len; i++) + { + data[i + 1] = _gnutls_cert_type2num (session->internals. + priorities.cert_type. + priority[i]); + } + return len + 1; + } + + } + else + { /* server side */ + if (session->security_parameters.cert_type != DEFAULT_CERT_TYPE) + { + len = 1; + if (data_size < len) + { + gnutls_assert (); + return GNUTLS_E_SHORT_MEMORY_BUFFER; + } + + data[0] = + _gnutls_cert_type2num (session->security_parameters.cert_type); + return len; + } + + + } + + return 0; +} + +/* Maps numbers to record sizes according to the + * extensions draft. + */ +inline static int +_gnutls_num2cert_type (int num) +{ + switch (num) + { + case 0: + return GNUTLS_CRT_X509; + case 1: + return GNUTLS_CRT_OPENPGP; + default: + return GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER; + } +} + +/* Maps record size to numbers according to the + * extensions draft. + */ +inline static int +_gnutls_cert_type2num (int cert_type) +{ + switch (cert_type) + { + case GNUTLS_CRT_X509: + return 0; + case GNUTLS_CRT_OPENPGP: + return 1; + default: + return GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER; + } + +} diff --git a/src/daemon/https/tls/ext_cert_type.h b/src/daemon/https/tls/ext_cert_type.h new file mode 100644 index 00000000..ea7cf219 --- /dev/null +++ b/src/daemon/https/tls/ext_cert_type.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +/* Maps record size to numbers according to the + * extensions draft. + */ +int _gnutls_cert_type_recv_params (gnutls_session_t session, + const opaque * data, size_t data_size); +int _gnutls_cert_type_send_params (gnutls_session_t session, opaque * data, + size_t); diff --git a/src/daemon/https/tls/ext_inner_application.c b/src/daemon/https/tls/ext_inner_application.c new file mode 100644 index 00000000..b86b7151 --- /dev/null +++ b/src/daemon/https/tls/ext_inner_application.c @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2005, 2006 Free Software Foundation + * + * Author: Simon Josefsson + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + +#include "gnutls_int.h" +#include "gnutls_auth_int.h" +#include "gnutls_errors.h" +#include "gnutls_num.h" +#include "ext_inner_application.h" + +#define NO 0 +#define YES 1 + +int +_gnutls_inner_application_recv_params (gnutls_session_t session, + const opaque * data, size_t data_size) +{ + tls_ext_st *ext = &session->security_parameters.extensions; + + if (data_size != 1) + { + gnutls_assert (); + return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; + } + + ext->gnutls_ia_peer_enable = 1; + ext->gnutls_ia_peer_allowskip = 0; + + switch ((unsigned char) *data) + { + case NO: /* Peer's ia_on_resume == no */ + ext->gnutls_ia_peer_allowskip = 1; + break; + + case YES: + break; + + default: + gnutls_assert (); + } + + return 0; +} + + +/* returns data_size or a negative number on failure + */ +int +_gnutls_inner_application_send_params (gnutls_session_t session, + opaque * data, size_t data_size) +{ + tls_ext_st *ext = &session->security_parameters.extensions; + + /* Set ext->gnutls_ia_enable depending on whether we have a TLS/IA + credential in the session. */ + + if (session->security_parameters.entity == GNUTLS_CLIENT) + { + gnutls_ia_client_credentials_t cred = (gnutls_ia_client_credentials_t) + _gnutls_get_cred (session->key, GNUTLS_CRD_IA, NULL); + + if (cred) + ext->gnutls_ia_enable = 1; + } + else + { + gnutls_ia_server_credentials_t cred = (gnutls_ia_server_credentials_t) + _gnutls_get_cred (session->key, GNUTLS_CRD_IA, NULL); + + if (cred) + ext->gnutls_ia_enable = 1; + } + + /* If we don't want gnutls_ia locally, or we are a server and the + * client doesn't want it, don't advertise TLS/IA support at all, as + * required. */ + + if (!ext->gnutls_ia_enable) + return 0; + + if (session->security_parameters.entity == GNUTLS_SERVER && + !ext->gnutls_ia_peer_enable) + return 0; + + /* We'll advertise. Check if there's room in the hello buffer. */ + + if (data_size < 1) + { + gnutls_assert (); + return GNUTLS_E_SHORT_MEMORY_BUFFER; + } + + /* default: require new application phase */ + + *data = YES; + + if (session->security_parameters.entity == GNUTLS_CLIENT) + { + + /* Client: value follows local setting */ + + if (ext->gnutls_ia_allowskip) + *data = NO; + } + else + { + + /* Server: value follows local setting and client's setting, but only + * if we are resuming. + * + * XXX Can server test for resumption at this stage? + * + * Ai! It seems that read_client_hello only calls parse_extensions if + * we're NOT resuming! That would make us automatically violate the IA + * draft; if we're resuming, we must first learn what the client wants + * -- IA or no IA -- and then prepare our response. Right now we'll + * always skip IA on resumption, because recv_ext isn't even called + * to record the peer's support for IA at all. Simon? */ + + if (ext->gnutls_ia_allowskip && + ext->gnutls_ia_peer_allowskip && + session->internals.resumed == RESUME_TRUE) + *data = NO; + } + + return 1; +} diff --git a/src/daemon/https/tls/ext_inner_application.h b/src/daemon/https/tls/ext_inner_application.h new file mode 100644 index 00000000..e75719e3 --- /dev/null +++ b/src/daemon/https/tls/ext_inner_application.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2005 Free Software Foundation + * + * Author: Simon Josefsson + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +int _gnutls_inner_application_recv_params (gnutls_session_t session, + const opaque * data, + size_t data_size); +int _gnutls_inner_application_send_params (gnutls_session_t session, + opaque * data, size_t); diff --git a/src/daemon/https/tls/ext_max_record.c b/src/daemon/https/tls/ext_max_record.c new file mode 100644 index 00000000..d0787483 --- /dev/null +++ b/src/daemon/https/tls/ext_max_record.c @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2001, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +/* This file contains the code for the Max Record Size TLS extension. + */ + +#include "gnutls_int.h" +#include "gnutls_errors.h" +#include "gnutls_num.h" +#include + +/* + * In case of a server: if a MAX_RECORD_SIZE extension type is received then it stores + * into the session the new value. The server may use gnutls_get_max_record_size(), + * in order to access it. + * + * In case of a client: If a different max record size (than the default) has + * been specified then it sends the extension. + * + */ + +int +_gnutls_max_record_recv_params (gnutls_session_t session, + const opaque * data, size_t _data_size) +{ + ssize_t new_size; + ssize_t data_size = _data_size; + + if (session->security_parameters.entity == GNUTLS_SERVER) + { + if (data_size > 0) + { + DECR_LEN (data_size, 1); + + new_size = _gnutls_mre_num2record (data[0]); + + if (new_size < 0) + { + gnutls_assert (); + return new_size; + } + + session->security_parameters.max_record_send_size = new_size; + session->security_parameters.max_record_recv_size = new_size; + } + } + else + { /* CLIENT SIDE - we must check if the sent record size is the right one + */ + if (data_size > 0) + { + + if (data_size != 1) + { + gnutls_assert (); + return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; + } + + new_size = _gnutls_mre_num2record (data[0]); + + if (new_size < 0 + || new_size != session->internals.proposed_record_size) + { + gnutls_assert (); + return GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER; + } + else + { + session->security_parameters.max_record_recv_size = + session->internals.proposed_record_size; + } + + } + + + } + + return 0; +} + +/* returns data_size or a negative number on failure + */ +int +_gnutls_max_record_send_params (gnutls_session_t session, opaque * data, + size_t data_size) +{ + uint16_t len; + /* this function sends the client extension data (dnsname) */ + if (session->security_parameters.entity == GNUTLS_CLIENT) + { + + if (session->internals.proposed_record_size != DEFAULT_MAX_RECORD_SIZE) + { + len = 1; + if (data_size < len) + { + gnutls_assert (); + return GNUTLS_E_SHORT_MEMORY_BUFFER; + } + + data[0] = + (uint8_t) _gnutls_mre_record2num (session->internals. + proposed_record_size); + return len; + } + + } + else + { /* server side */ + + if (session->security_parameters.max_record_recv_size != + DEFAULT_MAX_RECORD_SIZE) + { + len = 1; + if (data_size < len) + { + gnutls_assert (); + return GNUTLS_E_SHORT_MEMORY_BUFFER; + } + + data[0] = + (uint8_t) _gnutls_mre_record2num (session-> + security_parameters. + max_record_recv_size); + return len; + } + + + } + + return 0; +} + +/* Maps numbers to record sizes according to the + * extensions draft. + */ +int +_gnutls_mre_num2record (int num) +{ + switch (num) + { + case 1: + return 512; + case 2: + return 1024; + case 3: + return 2048; + case 4: + return 4096; + default: + return GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER; + } +} + +/* Maps record size to numbers according to the + * extensions draft. + */ +int +_gnutls_mre_record2num (uint16_t record_size) +{ + switch (record_size) + { + case 512: + return 1; + case 1024: + return 2; + case 2048: + return 3; + case 4096: + return 4; + default: + return GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER; + } + +} diff --git a/src/daemon/https/tls/ext_max_record.h b/src/daemon/https/tls/ext_max_record.h new file mode 100644 index 00000000..751bedc8 --- /dev/null +++ b/src/daemon/https/tls/ext_max_record.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2001, 2002, 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +/* Maps record size to numbers according to the + * extensions draft. + */ +int _gnutls_mre_num2record (int num); +int _gnutls_mre_record2num (uint16_t record_size); +int _gnutls_max_record_recv_params (gnutls_session_t session, + const opaque * data, size_t data_size); +int _gnutls_max_record_send_params (gnutls_session_t session, opaque * data, + size_t); diff --git a/src/daemon/https/tls/ext_oprfi.c b/src/daemon/https/tls/ext_oprfi.c new file mode 100644 index 00000000..c862ad1c --- /dev/null +++ b/src/daemon/https/tls/ext_oprfi.c @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2007 Free Software Foundation + * + * Author: Simon Josefsson + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +/* Implementation of Opaque PRF Input: + * http://tools.ietf.org/id/draft-rescorla-tls-opaque-prf-input-00.txt + * + */ + +#include + +#include +#include + +int +oprfi_recv_server (gnutls_session_t session, + const opaque * data, size_t _data_size) +{ + ssize_t data_size = _data_size; + uint16_t len; + int ret; + + if (!session->security_parameters.extensions.oprfi_cb) + { + gnutls_assert (); + return 0; + } + + DECR_LEN (data_size, 2); + len = _gnutls_read_uint16 (data); + data += 2; + + if (len != data_size) + { + gnutls_assert (); + return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; + } + + /* Store incoming data. */ + session->security_parameters.extensions.oprfi_client_len = len; + session->security_parameters.extensions.oprfi_client = gnutls_malloc (len); + if (!session->security_parameters.extensions.oprfi_client) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + memcpy (session->security_parameters.extensions.oprfi_client, data, len); + + return 0; +} + +int +oprfi_recv_client (gnutls_session_t session, + const opaque * data, size_t _data_size) +{ + ssize_t data_size = _data_size; + uint16_t len; + int ret; + + if (session->security_parameters.extensions.oprfi_client == NULL) + { + gnutls_assert (); + return 0; + } + + DECR_LEN (data_size, 2); + len = _gnutls_read_uint16 (data); + data += 2; + + if (len != data_size) + { + gnutls_assert (); + return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; + } + + if (len != session->security_parameters.extensions.oprfi_client_len) + { + gnutls_assert (); + return GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER; + } + + /* Store incoming data. */ + session->security_parameters.extensions.oprfi_server_len = len; + session->security_parameters.extensions.oprfi_server = gnutls_malloc (len); + if (!session->security_parameters.extensions.oprfi_server) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + memcpy (session->security_parameters.extensions.oprfi_server, data, len); + + return 0; +} + +int +_gnutls_oprfi_recv_params (gnutls_session_t session, + const opaque * data, size_t data_size) +{ + if (session->security_parameters.entity == GNUTLS_CLIENT) + return oprfi_recv_client (session, data, data_size); + else + return oprfi_recv_server (session, data, data_size); +} + +int +oprfi_send_client (gnutls_session_t session, opaque * data, size_t _data_size) +{ + opaque *p = data; + ssize_t data_size = _data_size; + int oprf_size = session->security_parameters.extensions.oprfi_client_len; + + if (oprf_size == 0) + return 0; + + DECR_LENGTH_RET (data_size, 2, GNUTLS_E_SHORT_MEMORY_BUFFER); + _gnutls_write_uint16 (oprf_size, p); + p += 2; + + DECR_LENGTH_RET (data_size, oprf_size, GNUTLS_E_SHORT_MEMORY_BUFFER); + + memcpy (p, session->security_parameters.extensions.oprfi_client, oprf_size); + + return 2 + oprf_size; +} + +int +oprfi_send_server (gnutls_session_t session, opaque * data, size_t _data_size) +{ + opaque *p = data; + int ret; + ssize_t data_size = _data_size; + size_t len; + + if (!session->security_parameters.extensions.oprfi_client || + !session->security_parameters.extensions.oprfi_cb) + return 0; + + /* Allocate buffer for outgoing data. */ + session->security_parameters.extensions.oprfi_server_len = + session->security_parameters.extensions.oprfi_client_len; + session->security_parameters.extensions.oprfi_server = + gnutls_malloc (session->security_parameters.extensions.oprfi_server_len); + if (!session->security_parameters.extensions.oprfi_server) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + /* Get outgoing data. */ + ret = session->security_parameters.extensions.oprfi_cb + (session, session->security_parameters.extensions.oprfi_userdata, + session->security_parameters.extensions.oprfi_client_len, + session->security_parameters.extensions.oprfi_client, + session->security_parameters.extensions.oprfi_server); + if (ret < 0) + { + gnutls_assert (); + gnutls_free (session->security_parameters.extensions.oprfi_server); + return ret; + } + + DECR_LENGTH_RET (data_size, 2, GNUTLS_E_SHORT_MEMORY_BUFFER); + _gnutls_write_uint16 (session->security_parameters. + extensions.oprfi_server_len, p); + p += 2; + + DECR_LENGTH_RET (data_size, session->security_parameters. + extensions.oprfi_server_len, GNUTLS_E_SHORT_MEMORY_BUFFER); + + memcpy (p, session->security_parameters.extensions.oprfi_server, + session->security_parameters.extensions.oprfi_server_len); + + return 2 + session->security_parameters.extensions.oprfi_server_len; +} + +int +_gnutls_oprfi_send_params (gnutls_session_t session, + opaque * data, size_t data_size) +{ + if (session->security_parameters.entity == GNUTLS_CLIENT) + return oprfi_send_client (session, data, data_size); + else + return oprfi_send_server (session, data, data_size); +} + +/** + * gnutls_oprfi_enable_client: + * @session: is a #gnutls_session_t structure. + * @len: length of Opaque PRF data to use in client. + * @data: Opaque PRF data to use in client. + * + * Request that the client should attempt to negotiate the Opaque PRF + * Input TLS extension, using the given data as the client's Opaque + * PRF input. + * + * The data is copied into the session context after this call, so you + * may de-allocate it immediately after calling this function. + **/ +void +gnutls_oprfi_enable_client (gnutls_session_t session, + size_t len, unsigned char *data) +{ + session->security_parameters.extensions.oprfi_client_len = len; + session->security_parameters.extensions.oprfi_client = data; +} + +/** + * gnutls_oprfi_enable_server: + * @session: is a #gnutls_session_t structure. + * @cb: function pointer to Opaque PRF extension server callback. + * @userdata: hook passed to callback function for passing application state. + * + * Request that the server should attempt to accept the Opaque PRF + * Input TLS extension. If the client requests the extension, the + * provided callback @cb will be invoked. The callback must have the + * following prototype: + * + * int callback (gnutls_session_t session, void *userdata, + * size_t oprfi_len, const unsigned char *in_oprfi, + * unsigned char *out_oprfi); + * + * The callback can inspect the client-provided data in the input + * parameters, and specify its own opaque prf input data in the output + * variable. The function must return 0 on success, otherwise the + * handshake will be aborted. + **/ +void +gnutls_oprfi_enable_server (gnutls_session_t session, + gnutls_oprfi_callback_func cb, void *userdata) +{ + session->security_parameters.extensions.oprfi_cb = cb; + session->security_parameters.extensions.oprfi_userdata = userdata; +} diff --git a/src/daemon/https/tls/ext_oprfi.h b/src/daemon/https/tls/ext_oprfi.h new file mode 100644 index 00000000..7f75be9c --- /dev/null +++ b/src/daemon/https/tls/ext_oprfi.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2007 Free Software Foundation + * + * Author: Simon Josefsson + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +#include + +int _gnutls_oprfi_recv_params (gnutls_session_t state, + const opaque * data, + size_t data_size); + +int _gnutls_oprfi_send_params (gnutls_session_t state, + opaque * data, + size_t data_size); diff --git a/src/daemon/https/tls/ext_server_name.c b/src/daemon/https/tls/ext_server_name.c new file mode 100644 index 00000000..bc6b4759 --- /dev/null +++ b/src/daemon/https/tls/ext_server_name.c @@ -0,0 +1,330 @@ +/* + * Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +#include "gnutls_int.h" +#include "gnutls_auth_int.h" +#include "gnutls_errors.h" +#include "gnutls_num.h" +#include + +/* + * In case of a server: if a NAME_DNS extension type is received then it stores + * into the session the value of NAME_DNS. The server may use gnutls_ext_get_server_name(), + * in order to access it. + * + * In case of a client: If a proper NAME_DNS extension type is found in the session then + * it sends the extension to the peer. + * + */ + +int +_gnutls_server_name_recv_params (gnutls_session_t session, + const opaque * data, size_t _data_size) +{ + int i; + const unsigned char *p; + uint16_t len, type; + ssize_t data_size = _data_size; + int server_names = 0; + + if (session->security_parameters.entity == GNUTLS_SERVER) + { + DECR_LENGTH_RET (data_size, 2, 0); + len = _gnutls_read_uint16 (data); + + if (len != data_size) + { + /* This is unexpected packet length, but + * just ignore it, for now. + */ + gnutls_assert (); + return 0; + } + + p = data + 2; + + /* Count all server_names in the packet. */ + while (data_size > 0) + { + DECR_LENGTH_RET (data_size, 1, 0); + p++; + + DECR_LEN (data_size, 2); + len = _gnutls_read_uint16 (p); + p += 2; + + DECR_LENGTH_RET (data_size, len, 0); + server_names++; + + p += len; + } + + session->security_parameters.extensions.server_names_size = + server_names; + if (server_names == 0) + return 0; /* no names found */ + + /* we cannot accept more server names. + */ + if (server_names > MAX_SERVER_NAME_EXTENSIONS) + server_names = MAX_SERVER_NAME_EXTENSIONS; + + p = data + 2; + for (i = 0; i < server_names; i++) + { + type = *p; + p++; + + len = _gnutls_read_uint16 (p); + p += 2; + + switch (type) + { + case 0: /* NAME_DNS */ + if (len <= MAX_SERVER_NAME_SIZE) + { + memcpy (session->security_parameters.extensions. + server_names[i].name, p, len); + session->security_parameters.extensions. + server_names[i].name_length = len; + session->security_parameters.extensions. + server_names[i].type = GNUTLS_NAME_DNS; + break; + } + } + + /* move to next record */ + p += len; + } + } + return 0; +} + +/* returns data_size or a negative number on failure + */ +int +_gnutls_server_name_send_params (gnutls_session_t session, + opaque * data, size_t _data_size) +{ + uint16_t len; + opaque *p; + unsigned i; + ssize_t data_size = _data_size; + int total_size = 0; + + /* this function sends the client extension data (dnsname) + */ + if (session->security_parameters.entity == GNUTLS_CLIENT) + { + + if (session->security_parameters.extensions.server_names_size == 0) + return 0; + + /* uint16_t + */ + total_size = 2; + for (i = 0; + i < session->security_parameters.extensions.server_names_size; i++) + { + /* count the total size + */ + len = + session->security_parameters.extensions.server_names[i]. + name_length; + + /* uint8_t + uint16_t + size + */ + total_size += 1 + 2 + len; + } + + p = data; + + /* UINT16: write total size of all names + */ + DECR_LENGTH_RET (data_size, 2, GNUTLS_E_SHORT_MEMORY_BUFFER); + _gnutls_write_uint16 (total_size - 2, p); + p += 2; + + for (i = 0; + i < session->security_parameters.extensions.server_names_size; i++) + { + + switch (session->security_parameters.extensions. + server_names[i].type) + { + case GNUTLS_NAME_DNS: + + len = + session->security_parameters.extensions. + server_names[i].name_length; + if (len == 0) + break; + + /* UINT8: type of this extension + * UINT16: size of the first name + * LEN: the actual server name. + */ + DECR_LENGTH_RET (data_size, len + 3, + GNUTLS_E_SHORT_MEMORY_BUFFER); + + *p = 0; /* NAME_DNS type */ + p++; + + _gnutls_write_uint16 (len, p); + p += 2; + + memcpy (p, + session->security_parameters.extensions. + server_names[0].name, len); + p += len; + break; + default: + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + } + } + + return total_size; +} + +/** + * gnutls_server_name_get - Used to get the server name indicator send by a client + * @session: is a #gnutls_session_t structure. + * @data: will hold the data + * @data_length: will hold the data length. Must hold the maximum size of data. + * @type: will hold the server name indicator type + * @indx: is the index of the server_name + * + * This function will allow you to get the name indication (if any), + * a client has sent. The name indication may be any of the enumeration + * gnutls_server_name_type_t. + * + * If @type is GNUTLS_NAME_DNS, then this function is to be used by servers + * that support virtual hosting, and the data will be a null terminated UTF-8 string. + * + * If @data has not enough size to hold the server name GNUTLS_E_SHORT_MEMORY_BUFFER + * is returned, and @data_length will hold the required size. + * + * @index is used to retrieve more than one server names (if sent by the client). + * The first server name has an index of 0, the second 1 and so on. If no name with the given + * index exists GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE is returned. + * + **/ +int +gnutls_server_name_get (gnutls_session_t session, void *data, + size_t * data_length, + unsigned int *type, unsigned int indx) +{ + char *_data = data; + + if (session->security_parameters.entity == GNUTLS_CLIENT) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + if (indx + 1 > session->security_parameters.extensions.server_names_size) + { + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + *type = session->security_parameters.extensions.server_names[indx].type; + + if (*data_length > /* greater since we need one extra byte for the null */ + session->security_parameters.extensions.server_names[indx].name_length) + { + *data_length = + session->security_parameters.extensions.server_names[indx]. + name_length; + memcpy (data, + session->security_parameters.extensions.server_names[indx]. + name, *data_length); + + if (*type == GNUTLS_NAME_DNS) /* null terminate */ + _data[(*data_length)] = 0; + + } + else + { + *data_length = + session->security_parameters.extensions.server_names[indx]. + name_length; + return GNUTLS_E_SHORT_MEMORY_BUFFER; + } + + return 0; +} + +/** + * gnutls_server_name_set - Used to set a name indicator to be sent as an extension + * @session: is a #gnutls_session_t structure. + * @type: specifies the indicator type + * @name: is a string that contains the server name. + * @name_length: holds the length of name + * + * This function is to be used by clients that want to inform + * (via a TLS extension mechanism) the server of the name they + * connected to. This should be used by clients that connect + * to servers that do virtual hosting. + * + * The value of @name depends on the @ind type. In case of GNUTLS_NAME_DNS, + * an ASCII or UTF-8 null terminated string, without the trailing dot, is expected. + * IPv4 or IPv6 addresses are not permitted. + * + **/ +int +gnutls_server_name_set (gnutls_session_t session, + gnutls_server_name_type_t type, + const void *name, size_t name_length) +{ + int server_names; + + if (session->security_parameters.entity == GNUTLS_SERVER) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + if (name_length > MAX_SERVER_NAME_SIZE) + return GNUTLS_E_SHORT_MEMORY_BUFFER; + + server_names = + session->security_parameters.extensions.server_names_size + 1; + + if (server_names > MAX_SERVER_NAME_EXTENSIONS) + server_names = MAX_SERVER_NAME_EXTENSIONS; + + session->security_parameters.extensions.server_names[server_names - + 1].type = type; + memcpy (session->security_parameters.extensions. + server_names[server_names - 1].name, name, name_length); + session->security_parameters.extensions.server_names[server_names - + 1].name_length = + name_length; + + session->security_parameters.extensions.server_names_size++; + + return 0; +} diff --git a/src/daemon/https/tls/ext_server_name.h b/src/daemon/https/tls/ext_server_name.h new file mode 100644 index 00000000..91e727a1 --- /dev/null +++ b/src/daemon/https/tls/ext_server_name.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +int _gnutls_server_name_recv_params (gnutls_session_t session, + const opaque * data, size_t data_size); +int _gnutls_server_name_send_params (gnutls_session_t session, + opaque * data, size_t); diff --git a/src/daemon/https/tls/gnutls.asn b/src/daemon/https/tls/gnutls.asn new file mode 100644 index 00000000..0cb98414 --- /dev/null +++ b/src/daemon/https/tls/gnutls.asn @@ -0,0 +1,93 @@ +GNUTLS { } + +DEFINITIONS EXPLICIT TAGS ::= + +BEGIN + +-- This file contains parts of PKCS-1 structures and some stuff +-- required for DSA keys. + +RSAPublicKey ::= SEQUENCE { + modulus INTEGER, -- n + publicExponent INTEGER -- e +} + +-- +-- Representation of RSA private key with information for the +-- CRT algorithm. +-- +RSAPrivateKey ::= SEQUENCE { + version Version, + modulus INTEGER, -- (Usually large) n + publicExponent INTEGER, -- (Usually small) e + privateExponent INTEGER, -- (Usually large) d + prime1 INTEGER, -- (Usually large) p + prime2 INTEGER, -- (Usually large) q + exponent1 INTEGER, -- (Usually large) d mod (p-1) + exponent2 INTEGER, -- (Usually large) d mod (q-1) + coefficient INTEGER, -- (Usually large) (inverse of q) mod p + otherPrimeInfos OtherPrimeInfos OPTIONAL +} + +Version ::= INTEGER { two-prime(0), multi(1) } +-- (CONSTRAINED BY { version must be multi if otherPrimeInfos present }) -- + +OtherPrimeInfos ::= SEQUENCE SIZE(1..MAX) OF OtherPrimeInfo + +OtherPrimeInfo ::= SEQUENCE { + prime INTEGER, -- ri + exponent INTEGER, -- di + coefficient INTEGER -- ti +} + +-- for signature calculation +-- added by nmav + +AlgorithmIdentifier ::= SEQUENCE { + algorithm OBJECT IDENTIFIER, + parameters ANY DEFINED BY algorithm OPTIONAL +} + -- contains a value of the type + -- registered for use with the + -- algorithm object identifier value + +DigestInfo ::= SEQUENCE { + digestAlgorithm DigestAlgorithmIdentifier, + digest Digest +} + +DigestAlgorithmIdentifier ::= AlgorithmIdentifier + +Digest ::= OCTET STRING + +DSAPublicKey ::= INTEGER + +DSAParameters ::= SEQUENCE { + p INTEGER, + q INTEGER, + g INTEGER +} + +DSASignatureValue ::= SEQUENCE { + r INTEGER, + s INTEGER +} + +DSAPrivateKey ::= SEQUENCE { + version INTEGER, -- should be zero + p INTEGER, + q INTEGER, + g INTEGER, + Y INTEGER, -- public + priv INTEGER +} + +-- from PKCS#3 +DHParameter ::= SEQUENCE { + prime INTEGER, -- p + base INTEGER, -- g + privateValueLength INTEGER OPTIONAL +} + + +END diff --git a/src/daemon/https/tls/gnutls.pc b/src/daemon/https/tls/gnutls.pc new file mode 100644 index 00000000..544428cb --- /dev/null +++ b/src/daemon/https/tls/gnutls.pc @@ -0,0 +1,23 @@ +# Process this file with autoconf to produce a pkg-config metadata file. +# Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation +# Author: Simon Josefsson +# +# 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. + +prefix=/home/lama/workbench/programming/c/gnunet/gnutls-2.2.3/build +exec_prefix=${prefix} +libdir=${exec_prefix}/lib +includedir=${prefix}/include + +Name: GnuTLS +Description: Transport Security Layer implementation for the GNU system +Version: 2.2.3 +Libs: -L${libdir} -lgnutls +Libs.private: -L${exec_prefix}/lib -lgnutls -L/usr/lib -ltasn1 -lgcrypt +Cflags: -I${includedir} diff --git a/src/daemon/https/tls/gnutls_alert.c b/src/daemon/https/tls/gnutls_alert.c new file mode 100644 index 00000000..75f9dcd2 --- /dev/null +++ b/src/daemon/https/tls/gnutls_alert.c @@ -0,0 +1,304 @@ +/* + * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +#include +#include +#include +#include + +typedef struct +{ + gnutls_alert_description_t alert; + const char *desc; +} gnutls_alert_entry; + +static const gnutls_alert_entry sup_alerts[] = { + {GNUTLS_A_CLOSE_NOTIFY, "Close notify"}, + {GNUTLS_A_UNEXPECTED_MESSAGE, "Unexpected message"}, + {GNUTLS_A_BAD_RECORD_MAC, "Bad record MAC"}, + {GNUTLS_A_DECRYPTION_FAILED, "Decryption failed"}, + {GNUTLS_A_RECORD_OVERFLOW, "Record overflow"}, + {GNUTLS_A_DECOMPRESSION_FAILURE, "Decompression failed"}, + {GNUTLS_A_HANDSHAKE_FAILURE, "Handshake failed"}, + {GNUTLS_A_BAD_CERTIFICATE, "Certificate is bad"}, + {GNUTLS_A_UNSUPPORTED_CERTIFICATE, "Certificate is not supported"}, + {GNUTLS_A_CERTIFICATE_REVOKED, "Certificate was revoked"}, + {GNUTLS_A_CERTIFICATE_EXPIRED, "Certificate is expired"}, + {GNUTLS_A_CERTIFICATE_UNKNOWN, "Unknown certificate"}, + {GNUTLS_A_ILLEGAL_PARAMETER, "Illegal parameter"}, + {GNUTLS_A_UNKNOWN_CA, "CA is unknown"}, + {GNUTLS_A_ACCESS_DENIED, "Access was denied"}, + {GNUTLS_A_DECODE_ERROR, "Decode error"}, + {GNUTLS_A_DECRYPT_ERROR, "Decrypt error"}, + {GNUTLS_A_EXPORT_RESTRICTION, "Export restriction"}, + {GNUTLS_A_PROTOCOL_VERSION, "Error in protocol version"}, + {GNUTLS_A_INSUFFICIENT_SECURITY, "Insufficient security"}, + {GNUTLS_A_USER_CANCELED, "User canceled"}, + {GNUTLS_A_INTERNAL_ERROR, "Internal error"}, + {GNUTLS_A_NO_RENEGOTIATION, "No renegotiation is allowed"}, + {GNUTLS_A_CERTIFICATE_UNOBTAINABLE, + "Could not retrieve the specified certificate"}, + {GNUTLS_A_UNSUPPORTED_EXTENSION, "An unsupported extension was sent"}, + {GNUTLS_A_UNRECOGNIZED_NAME, + "The server name sent was not recognized"}, + {GNUTLS_A_UNKNOWN_PSK_IDENTITY, + "The SRP/PSK username is missing or not known"}, + {GNUTLS_A_INNER_APPLICATION_FAILURE, + "Inner application negotiation failed"}, + {GNUTLS_A_INNER_APPLICATION_VERIFICATION, + "Inner application verification failed"}, + {0, NULL} +}; + +#define GNUTLS_ALERT_LOOP(b) \ + const gnutls_alert_entry *p; \ + for(p = sup_alerts; p->desc != NULL; p++) { b ; } + +#define GNUTLS_ALERT_ID_LOOP(a) \ + GNUTLS_ALERT_LOOP( if(p->alert == alert) { a; break; }) + + +/** + * gnutls_alert_get_name - Returns a string describing the alert number given + * @alert: is an alert number #gnutls_session_t structure. + * + * This function will return a string that describes the given alert + * number or NULL. See gnutls_alert_get(). + * + **/ +const char * +gnutls_alert_get_name (gnutls_alert_description_t alert) +{ + const char *ret = NULL; + + GNUTLS_ALERT_ID_LOOP (ret = p->desc); + + return ret; +} + +/** + * gnutls_alert_send - This function sends an alert message to the peer + * @session: is a #gnutls_session_t structure. + * @level: is the level of the alert + * @desc: is the alert description + * + * This function will send an alert to the peer in order to inform + * him of something important (eg. his Certificate could not be verified). + * If the alert level is Fatal then the peer is expected to close the + * connection, otherwise he may ignore the alert and continue. + * + * The error code of the underlying record send function will be returned, + * so you may also receive GNUTLS_E_INTERRUPTED or GNUTLS_E_AGAIN as well. + * + * Returns 0 on success. + * + **/ +int +gnutls_alert_send (gnutls_session_t session, gnutls_alert_level_t level, + gnutls_alert_description_t desc) +{ + uint8_t data[2]; + int ret; + const char *name; + + data[0] = (uint8_t) level; + data[1] = (uint8_t) desc; + + name = gnutls_alert_get_name ((int) data[1]); + if (name == NULL) + name = "(unknown)"; + _gnutls_record_log ("REC: Sending Alert[%d|%d] - %s\n", data[0], + data[1], name); + + if ((ret = _gnutls_send_int (session, GNUTLS_ALERT, -1, data, 2)) >= 0) + return 0; + else + return ret; +} + +/** + * gnutls_error_to_alert - This function returns an alert code based on the given error code + * @err: is a negative integer + * @level: the alert level will be stored there + * + * Returns an alert depending on the error code returned by a gnutls + * function. All alerts sent by this function should be considered fatal. + * The only exception is when err == GNUTLS_E_REHANDSHAKE, where a warning + * alert should be sent to the peer indicating that no renegotiation will + * be performed. + * + * If there is no mapping to a valid alert the alert to indicate internal error + * is returned. + * + **/ +int +gnutls_error_to_alert (int err, int *level) +{ + int ret, _level = -1; + + switch (err) + { /* send appropriate alert */ + case GNUTLS_E_DECRYPTION_FAILED: + /* GNUTLS_A_DECRYPTION_FAILED is not sent, because + * it is not defined in SSL3. Note that we must + * not distinguish Decryption failures from mac + * check failures, due to the possibility of some + * attacks. + */ + ret = GNUTLS_A_BAD_RECORD_MAC; + _level = GNUTLS_AL_FATAL; + break; + case GNUTLS_E_DECOMPRESSION_FAILED: + ret = GNUTLS_A_DECOMPRESSION_FAILURE; + _level = GNUTLS_AL_FATAL; + break; + case GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER: + case GNUTLS_E_ILLEGAL_SRP_USERNAME: + ret = GNUTLS_A_ILLEGAL_PARAMETER; + _level = GNUTLS_AL_FATAL; + break; + case GNUTLS_E_ASN1_ELEMENT_NOT_FOUND: + case GNUTLS_E_ASN1_IDENTIFIER_NOT_FOUND: + case GNUTLS_E_ASN1_DER_ERROR: + case GNUTLS_E_ASN1_VALUE_NOT_FOUND: + case GNUTLS_E_ASN1_GENERIC_ERROR: + case GNUTLS_E_ASN1_VALUE_NOT_VALID: + case GNUTLS_E_ASN1_TAG_ERROR: + case GNUTLS_E_ASN1_TAG_IMPLICIT: + case GNUTLS_E_ASN1_TYPE_ANY_ERROR: + case GNUTLS_E_ASN1_SYNTAX_ERROR: + case GNUTLS_E_ASN1_DER_OVERFLOW: + ret = GNUTLS_A_BAD_CERTIFICATE; + _level = GNUTLS_AL_FATAL; + break; + case GNUTLS_E_UNKNOWN_CIPHER_SUITE: + case GNUTLS_E_UNKNOWN_COMPRESSION_ALGORITHM: + case GNUTLS_E_INSUFFICIENT_CREDENTIALS: + case GNUTLS_E_NO_CIPHER_SUITES: + case GNUTLS_E_NO_COMPRESSION_ALGORITHMS: + ret = GNUTLS_A_HANDSHAKE_FAILURE; + _level = GNUTLS_AL_FATAL; + break; + case GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION: + ret = GNUTLS_A_UNSUPPORTED_EXTENSION; + _level = GNUTLS_AL_FATAL; + break; + case GNUTLS_E_UNEXPECTED_PACKET: + case GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET: + ret = GNUTLS_A_UNEXPECTED_MESSAGE; + _level = GNUTLS_AL_FATAL; + break; + case GNUTLS_E_REHANDSHAKE: + ret = GNUTLS_A_NO_RENEGOTIATION; + _level = GNUTLS_AL_WARNING; + break; + case GNUTLS_E_UNSUPPORTED_VERSION_PACKET: + ret = GNUTLS_A_PROTOCOL_VERSION; + _level = GNUTLS_AL_FATAL; + break; + case GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE: + ret = GNUTLS_A_UNSUPPORTED_CERTIFICATE; + _level = GNUTLS_AL_FATAL; + break; + case GNUTLS_E_UNEXPECTED_PACKET_LENGTH: + ret = GNUTLS_A_RECORD_OVERFLOW; + _level = GNUTLS_AL_FATAL; + break; + case GNUTLS_E_INTERNAL_ERROR: + case GNUTLS_E_NO_TEMPORARY_DH_PARAMS: + case GNUTLS_E_NO_TEMPORARY_RSA_PARAMS: + ret = GNUTLS_A_INTERNAL_ERROR; + _level = GNUTLS_AL_FATAL; + break; + case GNUTLS_E_OPENPGP_GETKEY_FAILED: + ret = GNUTLS_A_CERTIFICATE_UNOBTAINABLE; + _level = GNUTLS_AL_FATAL; + break; + case GNUTLS_E_DH_PRIME_UNACCEPTABLE: + case GNUTLS_E_NO_CERTIFICATE_FOUND: + ret = GNUTLS_A_INSUFFICIENT_SECURITY; + _level = GNUTLS_AL_FATAL; + break; + default: + ret = GNUTLS_A_INTERNAL_ERROR; + _level = GNUTLS_AL_FATAL; + break; + } + + if (level != NULL) + *level = _level; + + return ret; +} + + +/** + * gnutls_alert_send_appropriate - This function sends an alert to the peer depending on the error code + * @session: is a #gnutls_session_t structure. + * @err: is an integer + * + * Sends an alert to the peer depending on the error code returned by a gnutls + * function. This function will call gnutls_error_to_alert() to determine + * the appropriate alert to send. + * + * This function may also return GNUTLS_E_AGAIN, or GNUTLS_E_INTERRUPTED. + * + * If the return value is GNUTLS_E_INVALID_REQUEST, then no alert has + * been sent to the peer. + * + * Returns zero on success. + */ +int +gnutls_alert_send_appropriate (gnutls_session_t session, int err) +{ + int alert; + int level; + + alert = gnutls_error_to_alert (err, &level); + if (alert < 0) + { + return alert; + } + + return gnutls_alert_send (session, level, alert); +} + +/** + * gnutls_alert_get - Returns the last alert number received. + * @session: is a #gnutls_session_t structure. + * + * This function will return the last alert number received. This + * function should be called if GNUTLS_E_WARNING_ALERT_RECEIVED or + * GNUTLS_E_FATAL_ALERT_RECEIVED has been returned by a gnutls + * function. The peer may send alerts if he thinks some things were + * not right. Check gnutls.h for the available alert descriptions. + * + * If no alert has been received the returned value is undefined. + * + **/ +gnutls_alert_description_t +gnutls_alert_get (gnutls_session_t session) +{ + return session->internals.last_alert; +} diff --git a/src/daemon/https/tls/gnutls_algorithms.c b/src/daemon/https/tls/gnutls_algorithms.c new file mode 100644 index 00000000..82bf9bf6 --- /dev/null +++ b/src/daemon/https/tls/gnutls_algorithms.c @@ -0,0 +1,2225 @@ +/* + * Copyright (C) 2000, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +#include "gnutls_int.h" +#include "gnutls_algorithms.h" +#include "gnutls_errors.h" +#include "gnutls_cert.h" +/* x509 */ +#include "common.h" + +/* Cred type mappings to KX algorithms + * FIXME: The mappings are not 1-1. Some KX such as SRP_RSA require + * more than one credentials type. + */ +typedef struct +{ + gnutls_kx_algorithm_t algorithm; + gnutls_credentials_type_t client_type; + gnutls_credentials_type_t server_type; /* The type of credentials a server + * needs to set */ +} gnutls_cred_map; + +static const gnutls_cred_map cred_mappings[] = { + {GNUTLS_KX_ANON_DH, + GNUTLS_CRD_ANON, + GNUTLS_CRD_ANON}, + {GNUTLS_KX_RSA, + GNUTLS_CRD_CERTIFICATE, + GNUTLS_CRD_CERTIFICATE}, + {GNUTLS_KX_RSA_EXPORT, + GNUTLS_CRD_CERTIFICATE, + GNUTLS_CRD_CERTIFICATE}, + {GNUTLS_KX_DHE_DSS, + GNUTLS_CRD_CERTIFICATE, + GNUTLS_CRD_CERTIFICATE}, + {GNUTLS_KX_DHE_RSA, + GNUTLS_CRD_CERTIFICATE, + GNUTLS_CRD_CERTIFICATE}, + {GNUTLS_KX_PSK, + GNUTLS_CRD_PSK, + GNUTLS_CRD_PSK}, + {GNUTLS_KX_DHE_PSK, + GNUTLS_CRD_PSK, + GNUTLS_CRD_PSK}, + {GNUTLS_KX_SRP, + GNUTLS_CRD_SRP, + GNUTLS_CRD_SRP}, + {GNUTLS_KX_SRP_RSA, + GNUTLS_CRD_SRP, + GNUTLS_CRD_CERTIFICATE}, + {GNUTLS_KX_SRP_DSS, + GNUTLS_CRD_SRP, + GNUTLS_CRD_CERTIFICATE}, + {0, + 0, + 0} +}; + +#define GNUTLS_KX_MAP_LOOP(b) \ + const gnutls_cred_map *p; \ + for(p = cred_mappings; p->algorithm != 0; p++) { b ; } + +#define GNUTLS_KX_MAP_ALG_LOOP_SERVER(a) \ + GNUTLS_KX_MAP_LOOP( if(p->server_type == type) { a; break; }) + +#define GNUTLS_KX_MAP_ALG_LOOP_CLIENT(a) \ + GNUTLS_KX_MAP_LOOP( if(p->client_type == type) { a; break; }) + +/* KX mappings to PK algorithms */ +typedef struct +{ + gnutls_kx_algorithm_t kx_algorithm; + gnutls_pk_algorithm_t pk_algorithm; + enum encipher_type encipher_type; /* CIPHER_ENCRYPT if this algorithm is to be used + * for encryption, CIPHER_SIGN if signature only, + * CIPHER_IGN if this does not apply at all. + * + * This is useful to certificate cipher suites, which check + * against the certificate key usage bits. + */ +} gnutls_pk_map; + +/* This table maps the Key exchange algorithms to + * the certificate algorithms. Eg. if we have + * RSA algorithm in the certificate then we can + * use GNUTLS_KX_RSA or GNUTLS_KX_DHE_RSA. + */ +static const gnutls_pk_map pk_mappings[] = { + {GNUTLS_KX_RSA, + GNUTLS_PK_RSA, + CIPHER_ENCRYPT}, + {GNUTLS_KX_RSA_EXPORT, + GNUTLS_PK_RSA, + CIPHER_SIGN}, + {GNUTLS_KX_DHE_RSA, + GNUTLS_PK_RSA, + CIPHER_SIGN}, + {GNUTLS_KX_SRP_RSA, + GNUTLS_PK_RSA, + CIPHER_SIGN}, + {0, + 0, + 0} +}; + +#define GNUTLS_PK_MAP_LOOP(b) \ + const gnutls_pk_map *p; \ + for(p = pk_mappings; p->kx_algorithm != 0; p++) { b } + +#define GNUTLS_PK_MAP_ALG_LOOP(a) \ + GNUTLS_PK_MAP_LOOP( if(p->kx_algorithm == kx_algorithm) { a; break; }) + +/* TLS Versions */ + +typedef struct +{ + const char *name; + gnutls_protocol_t id; /* gnutls internal version number */ + int major; /* defined by the protocol */ + int minor; /* defined by the protocol */ + int supported; /* 0 not supported, > 0 is supported */ +} gnutls_version_entry; + +static const gnutls_version_entry sup_versions[] = { + {"SSL3.0", + GNUTLS_SSL3, + 3, + 0, + 1}, + {"TLS1.0", + GNUTLS_TLS1, + 3, + 1, + 1}, + {"TLS1.1", + GNUTLS_TLS1_1, + 3, + 2, + 1}, + {"TLS1.2", + GNUTLS_TLS1_2, + 3, + 3, + 1}, + {0, + 0, + 0, + 0, + 0} +}; + +/* Keep the contents of this struct the same as the previous one. */ +static const gnutls_protocol_t supported_protocols[] = { GNUTLS_SSL3, + GNUTLS_TLS1, + GNUTLS_TLS1_1, + GNUTLS_TLS1_2, + 0 +}; + +#define GNUTLS_VERSION_LOOP(b) \ + const gnutls_version_entry *p; \ + for(p = sup_versions; p->name != NULL; p++) { b ; } + +#define GNUTLS_VERSION_ALG_LOOP(a) \ + GNUTLS_VERSION_LOOP( if(p->id == version) { a; break; }) + +struct gnutls_cipher_entry +{ + const char *name; + gnutls_cipher_algorithm_t id; + uint16_t blocksize; + uint16_t keysize; + cipher_type_t block; + uint16_t iv; + int export_flag; /* 0 non export */ +}; +typedef struct gnutls_cipher_entry gnutls_cipher_entry; + +/* Note that all algorithms are in CBC or STREAM modes. + * Do not add any algorithms in other modes (avoid modified algorithms). + * View first: "The order of encryption and authentication for + * protecting communications" by Hugo Krawczyk - CRYPTO 2001 + */ +static const gnutls_cipher_entry algorithms[] = { + {"AES-256-CBC", + GNUTLS_CIPHER_AES_256_CBC, + 16, + 32, + CIPHER_BLOCK, + 16, + 0}, + {"AES-128-CBC", + GNUTLS_CIPHER_AES_128_CBC, + 16, + 16, + CIPHER_BLOCK, + 16, + 0}, + {"3DES-CBC", + GNUTLS_CIPHER_3DES_CBC, + 8, + 24, + CIPHER_BLOCK, + 8, + 0}, + {"DES-CBC", + GNUTLS_CIPHER_DES_CBC, + 8, + 8, + CIPHER_BLOCK, + 8, + 0}, + {"ARCFOUR-128", + GNUTLS_CIPHER_ARCFOUR_128, + 1, + 16, + CIPHER_STREAM, + 0, + 0}, + {"ARCFOUR-40", + GNUTLS_CIPHER_ARCFOUR_40, + 1, + 5, + CIPHER_STREAM, + 0, + 1}, + {"RC2-40", + GNUTLS_CIPHER_RC2_40_CBC, + 8, + 5, + CIPHER_BLOCK, + 8, + 1}, +#ifdef ENABLE_CAMELLIA + {"CAMELLIA-256-CBC", GNUTLS_CIPHER_CAMELLIA_256_CBC, 16, 32, CIPHER_BLOCK, + 16, 0}, + {"CAMELLIA-128-CBC", GNUTLS_CIPHER_CAMELLIA_128_CBC, 16, 16, CIPHER_BLOCK, + 16, 0}, +#endif + {"NULL", + GNUTLS_CIPHER_NULL, + 1, + 0, + CIPHER_STREAM, + 0, + 0}, + {0, + 0, + 0, + 0, + 0, + 0, + 0} +}; + +/* Keep the contents of this struct the same as the previous one. */ +static const gnutls_cipher_algorithm_t supported_ciphers[] = + { GNUTLS_CIPHER_AES_256_CBC, + GNUTLS_CIPHER_AES_128_CBC, + GNUTLS_CIPHER_3DES_CBC, + GNUTLS_CIPHER_DES_CBC, + GNUTLS_CIPHER_ARCFOUR_128, + GNUTLS_CIPHER_ARCFOUR_40, + GNUTLS_CIPHER_RC2_40_CBC, +#ifdef ENABLE_CAMELLIA + GNUTLS_CIPHER_CAMELLIA_256_CBC, + GNUTLS_CIPHER_CAMELLIA_128_CBC, +#endif + GNUTLS_CIPHER_NULL, + 0 +}; + +#define GNUTLS_LOOP(b) \ + const gnutls_cipher_entry *p; \ + for(p = algorithms; p->name != NULL; p++) { b ; } + +#define GNUTLS_ALG_LOOP(a) \ + GNUTLS_LOOP( if(p->id == algorithm) { a; break; } ) + +struct gnutls_hash_entry +{ + const char *name; + const char *oid; + gnutls_mac_algorithm_t id; + size_t key_size; /* in case of mac */ +}; +typedef struct gnutls_hash_entry gnutls_hash_entry; + +static const gnutls_hash_entry hash_algorithms[] = { + {"SHA1", + HASH_OID_SHA1, + GNUTLS_MAC_SHA1, + 20}, + {"MD5", + HASH_OID_MD5, + GNUTLS_MAC_MD5, + 16}, + {"SHA256", + HASH_OID_SHA256, + GNUTLS_MAC_SHA256, + 32}, + {"NULL", + NULL, + GNUTLS_MAC_NULL, + 0}, + {0, + 0, + 0, + 0} +}; + +/* Keep the contents of this struct the same as the previous one. */ +static const gnutls_mac_algorithm_t supported_macs[] = { GNUTLS_MAC_SHA1, + GNUTLS_MAC_MD5, + GNUTLS_MAC_SHA256, + GNUTLS_MAC_NULL, + 0 +}; + +#define GNUTLS_HASH_LOOP(b) \ + const gnutls_hash_entry *p; \ + for(p = hash_algorithms; p->name != NULL; p++) { b ; } + +#define GNUTLS_HASH_ALG_LOOP(a) \ + GNUTLS_HASH_LOOP( if(p->id == algorithm) { a; break; } ) + +/* Compression Section */ +#define GNUTLS_COMPRESSION_ENTRY(name, id, wb, ml, cl) \ + { #name, name, id, wb, ml, cl} + +#define MAX_COMP_METHODS 5 +const int _gnutls_comp_algorithms_size = MAX_COMP_METHODS; + +/* the compression entry is defined in gnutls_algorithms.h */ + +gnutls_compression_entry _gnutls_compression_algorithms[MAX_COMP_METHODS] = + { GNUTLS_COMPRESSION_ENTRY (GNUTLS_COMP_NULL, 0x00, 0, 0, 0), +#ifdef HAVE_LIBZ + /* draft-ietf-tls-compression-02 */ + GNUTLS_COMPRESSION_ENTRY (GNUTLS_COMP_DEFLATE, 0x01, 15, 8, 3), +#endif + {0, + 0, + 0, + 0, + 0, + 0} +}; + +static const gnutls_compression_method_t supported_compressions[] = { +#ifdef HAVE_LIBZ + GNUTLS_COMP_DEFLATE, +#endif + GNUTLS_COMP_NULL, + 0 +}; + +#define GNUTLS_COMPRESSION_LOOP(b) \ + const gnutls_compression_entry *p; \ + for(p = _gnutls_compression_algorithms; p->name != NULL; p++) { b ; } +#define GNUTLS_COMPRESSION_ALG_LOOP(a) \ + GNUTLS_COMPRESSION_LOOP( if(p->id == algorithm) { a; break; } ) +#define GNUTLS_COMPRESSION_ALG_LOOP_NUM(a) \ + GNUTLS_COMPRESSION_LOOP( if(p->num == num) { a; break; } ) + +/* Key Exchange Section */ +extern mod_auth_st rsa_auth_struct; +extern mod_auth_st rsa_export_auth_struct; +extern mod_auth_st dhe_rsa_auth_struct; +extern mod_auth_st dhe_dss_auth_struct; +extern mod_auth_st anon_auth_struct; +extern mod_auth_st srp_auth_struct; +extern mod_auth_st psk_auth_struct; +extern mod_auth_st dhe_psk_auth_struct; +extern mod_auth_st srp_rsa_auth_struct; +extern mod_auth_st srp_dss_auth_struct; + +struct gnutls_kx_algo_entry +{ + const char *name; + gnutls_kx_algorithm_t algorithm; + mod_auth_st *auth_struct; + int needs_dh_params; + int needs_rsa_params; +}; +typedef struct gnutls_kx_algo_entry gnutls_kx_algo_entry; + +static const gnutls_kx_algo_entry _gnutls_kx_algorithms[] = { +#ifdef ENABLE_ANON + {"ANON-DH", GNUTLS_KX_ANON_DH, &anon_auth_struct, 1, 0}, +#endif + {"RSA", + GNUTLS_KX_RSA, + &rsa_auth_struct, + 0, + 0}, + {"RSA-EXPORT", + GNUTLS_KX_RSA_EXPORT, + &rsa_export_auth_struct, + 0, + 1 /* needs RSA params */ }, + {"DHE-RSA", + GNUTLS_KX_DHE_RSA, + &dhe_rsa_auth_struct, + 1, + 0}, + {"DHE-DSS", + GNUTLS_KX_DHE_DSS, + &dhe_dss_auth_struct, + 1, + 0}, + +#ifdef ENABLE_SRP + {"SRP-DSS", GNUTLS_KX_SRP_DSS, &srp_dss_auth_struct, 0, 0}, + {"SRP-RSA", GNUTLS_KX_SRP_RSA, &srp_rsa_auth_struct, 0, 0}, + {"SRP", GNUTLS_KX_SRP, &srp_auth_struct, 0, 0}, +#endif +#ifdef ENABLE_PSK + {"PSK", GNUTLS_KX_PSK, &psk_auth_struct, 0, 0}, + {"DHE-PSK", GNUTLS_KX_DHE_PSK, &dhe_psk_auth_struct, + 1 /* needs DHE params */ , 0}, +#endif + {0, + 0, + 0, + 0, + 0} +}; + +/* Keep the contents of this struct the same as the previous one. */ +static const gnutls_kx_algorithm_t supported_kxs[] = { +#ifdef ENABLE_ANON + GNUTLS_KX_ANON_DH, +#endif + GNUTLS_KX_RSA, + GNUTLS_KX_RSA_EXPORT, + GNUTLS_KX_DHE_RSA, + GNUTLS_KX_DHE_DSS, +#ifdef ENABLE_SRP + GNUTLS_KX_SRP_DSS, + GNUTLS_KX_SRP_RSA, + GNUTLS_KX_SRP, +#endif +#ifdef ENABLE_PSK + GNUTLS_KX_PSK, + GNUTLS_KX_DHE_PSK, +#endif + 0 +}; + +#define GNUTLS_KX_LOOP(b) \ + const gnutls_kx_algo_entry *p; \ + for(p = _gnutls_kx_algorithms; p->name != NULL; p++) { b ; } + +#define GNUTLS_KX_ALG_LOOP(a) \ + GNUTLS_KX_LOOP( if(p->algorithm == algorithm) { a; break; } ) + +/* Cipher SUITES */ +#define GNUTLS_CIPHER_SUITE_ENTRY( name, block_algorithm, kx_algorithm, mac_algorithm, version ) \ + { #name, {name}, block_algorithm, kx_algorithm, mac_algorithm, version } + +typedef struct +{ + const char *name; + cipher_suite_st id; + gnutls_cipher_algorithm_t block_algorithm; + gnutls_kx_algorithm_t kx_algorithm; + gnutls_mac_algorithm_t mac_algorithm; + gnutls_protocol_t version; /* this cipher suite is supported + * from 'version' and above; + */ +} gnutls_cipher_suite_entry; + +/* RSA with NULL cipher and MD5 MAC + * for test purposes. + */ +#define GNUTLS_RSA_NULL_MD5 { 0x00, 0x01 } + +/* ANONymous cipher suites. + */ + +#define GNUTLS_ANON_DH_3DES_EDE_CBC_SHA1 { 0x00, 0x1B } +#define GNUTLS_ANON_DH_ARCFOUR_MD5 { 0x00, 0x18 } + +/* rfc3268: */ +#define GNUTLS_ANON_DH_AES_128_CBC_SHA1 { 0x00, 0x34 } +#define GNUTLS_ANON_DH_AES_256_CBC_SHA1 { 0x00, 0x3A } + +/* rfc4132 */ +#define GNUTLS_ANON_DH_CAMELLIA_128_CBC_SHA1 { 0x00,0x46 } +#define GNUTLS_ANON_DH_CAMELLIA_256_CBC_SHA1 { 0x00,0x89 } + +/* PSK (not in TLS 1.0) + * draft-ietf-tls-psk: + */ +#define GNUTLS_PSK_SHA_ARCFOUR_SHA1 { 0x00, 0x8A } +#define GNUTLS_PSK_SHA_3DES_EDE_CBC_SHA1 { 0x00, 0x8B } +#define GNUTLS_PSK_SHA_AES_128_CBC_SHA1 { 0x00, 0x8C } +#define GNUTLS_PSK_SHA_AES_256_CBC_SHA1 { 0x00, 0x8D } + +#define GNUTLS_DHE_PSK_SHA_ARCFOUR_SHA1 { 0x00, 0x8E } +#define GNUTLS_DHE_PSK_SHA_3DES_EDE_CBC_SHA1 { 0x00, 0x8F } +#define GNUTLS_DHE_PSK_SHA_AES_128_CBC_SHA1 { 0x00, 0x90 } +#define GNUTLS_DHE_PSK_SHA_AES_256_CBC_SHA1 { 0x00, 0x91 } + +/* SRP (rfc5054) + */ +#define GNUTLS_SRP_SHA_3DES_EDE_CBC_SHA1 { 0xC0, 0x1A } +#define GNUTLS_SRP_SHA_RSA_3DES_EDE_CBC_SHA1 { 0xC0, 0x1B } +#define GNUTLS_SRP_SHA_DSS_3DES_EDE_CBC_SHA1 { 0xC0, 0x1C } + +#define GNUTLS_SRP_SHA_AES_128_CBC_SHA1 { 0xC0, 0x1D } +#define GNUTLS_SRP_SHA_RSA_AES_128_CBC_SHA1 { 0xC0, 0x1E } +#define GNUTLS_SRP_SHA_DSS_AES_128_CBC_SHA1 { 0xC0, 0x1F } + +#define GNUTLS_SRP_SHA_AES_256_CBC_SHA1 { 0xC0, 0x20 } +#define GNUTLS_SRP_SHA_RSA_AES_256_CBC_SHA1 { 0xC0, 0x21 } +#define GNUTLS_SRP_SHA_DSS_AES_256_CBC_SHA1 { 0xC0, 0x22 } + +/* RSA + */ +#define GNUTLS_RSA_ARCFOUR_SHA1 { 0x00, 0x05 } +#define GNUTLS_RSA_ARCFOUR_MD5 { 0x00, 0x04 } +#define GNUTLS_RSA_3DES_EDE_CBC_SHA1 { 0x00, 0x0A } + +#define GNUTLS_RSA_EXPORT_ARCFOUR_40_MD5 { 0x00, 0x03 } + +/* rfc3268: + */ +#define GNUTLS_RSA_AES_128_CBC_SHA1 { 0x00, 0x2F } +#define GNUTLS_RSA_AES_256_CBC_SHA1 { 0x00, 0x35 } + +/* rfc4132 */ +#define GNUTLS_RSA_CAMELLIA_128_CBC_SHA1 { 0x00,0x41 } +#define GNUTLS_RSA_CAMELLIA_256_CBC_SHA1 { 0x00,0x84 } + +/* DHE DSS + */ + +#define GNUTLS_DHE_DSS_3DES_EDE_CBC_SHA1 { 0x00, 0x13 } + +/* draft-ietf-tls-56-bit-ciphersuites-01: + */ +#define GNUTLS_DHE_DSS_ARCFOUR_SHA1 { 0x00, 0x66 } + +/* rfc3268: + */ +#define GNUTLS_DHE_DSS_AES_256_CBC_SHA1 { 0x00, 0x38 } +#define GNUTLS_DHE_DSS_AES_128_CBC_SHA1 { 0x00, 0x32 } + +/* rfc4132 */ +#define GNUTLS_DHE_DSS_CAMELLIA_128_CBC_SHA1 { 0x00,0x44 } +#define GNUTLS_DHE_DSS_CAMELLIA_256_CBC_SHA1 { 0x00,0x87 } + +/* DHE RSA + */ +#define GNUTLS_DHE_RSA_3DES_EDE_CBC_SHA1 { 0x00, 0x16 } + +/* rfc3268: + */ +#define GNUTLS_DHE_RSA_AES_128_CBC_SHA1 { 0x00, 0x33 } +#define GNUTLS_DHE_RSA_AES_256_CBC_SHA1 { 0x00, 0x39 } + +/* rfc4132 */ +#define GNUTLS_DHE_RSA_CAMELLIA_128_CBC_SHA1 { 0x00,0x45 } +#define GNUTLS_DHE_RSA_CAMELLIA_256_CBC_SHA1 { 0x00,0x88 } + +#define CIPHER_SUITES_COUNT sizeof(cs_algorithms)/sizeof(gnutls_cipher_suite_entry)-1 + +static const gnutls_cipher_suite_entry cs_algorithms[] = { + /* ANON_DH */ + GNUTLS_CIPHER_SUITE_ENTRY (GNUTLS_ANON_DH_ARCFOUR_MD5, + GNUTLS_CIPHER_ARCFOUR_128, + GNUTLS_KX_ANON_DH, GNUTLS_MAC_MD5, + GNUTLS_SSL3), + GNUTLS_CIPHER_SUITE_ENTRY (GNUTLS_ANON_DH_3DES_EDE_CBC_SHA1, + GNUTLS_CIPHER_3DES_CBC, GNUTLS_KX_ANON_DH, + GNUTLS_MAC_SHA1, GNUTLS_SSL3), + GNUTLS_CIPHER_SUITE_ENTRY (GNUTLS_ANON_DH_AES_128_CBC_SHA1, + GNUTLS_CIPHER_AES_128_CBC, GNUTLS_KX_ANON_DH, + GNUTLS_MAC_SHA1, GNUTLS_SSL3), + GNUTLS_CIPHER_SUITE_ENTRY (GNUTLS_ANON_DH_AES_256_CBC_SHA1, + GNUTLS_CIPHER_AES_256_CBC, GNUTLS_KX_ANON_DH, + GNUTLS_MAC_SHA1, GNUTLS_SSL3), +#ifdef ENABLE_CAMELLIA + GNUTLS_CIPHER_SUITE_ENTRY (GNUTLS_ANON_DH_CAMELLIA_128_CBC_SHA1, + GNUTLS_CIPHER_CAMELLIA_128_CBC, + GNUTLS_KX_ANON_DH, + GNUTLS_MAC_SHA1, GNUTLS_TLS1), + GNUTLS_CIPHER_SUITE_ENTRY (GNUTLS_ANON_DH_CAMELLIA_256_CBC_SHA1, + GNUTLS_CIPHER_CAMELLIA_256_CBC, + GNUTLS_KX_ANON_DH, + GNUTLS_MAC_SHA1, GNUTLS_TLS1), +#endif + + /* PSK */ + GNUTLS_CIPHER_SUITE_ENTRY (GNUTLS_PSK_SHA_ARCFOUR_SHA1, + GNUTLS_CIPHER_ARCFOUR, GNUTLS_KX_PSK, + GNUTLS_MAC_SHA1, GNUTLS_TLS1), + GNUTLS_CIPHER_SUITE_ENTRY (GNUTLS_PSK_SHA_3DES_EDE_CBC_SHA1, + GNUTLS_CIPHER_3DES_CBC, GNUTLS_KX_PSK, + GNUTLS_MAC_SHA1, GNUTLS_TLS1), + GNUTLS_CIPHER_SUITE_ENTRY (GNUTLS_PSK_SHA_AES_128_CBC_SHA1, + GNUTLS_CIPHER_AES_128_CBC, GNUTLS_KX_PSK, + GNUTLS_MAC_SHA1, GNUTLS_TLS1), + GNUTLS_CIPHER_SUITE_ENTRY (GNUTLS_PSK_SHA_AES_256_CBC_SHA1, + GNUTLS_CIPHER_AES_256_CBC, GNUTLS_KX_PSK, + GNUTLS_MAC_SHA1, GNUTLS_TLS1), + + /* DHE-PSK */ + GNUTLS_CIPHER_SUITE_ENTRY (GNUTLS_DHE_PSK_SHA_ARCFOUR_SHA1, + GNUTLS_CIPHER_ARCFOUR, GNUTLS_KX_DHE_PSK, + GNUTLS_MAC_SHA1, GNUTLS_TLS1), + GNUTLS_CIPHER_SUITE_ENTRY (GNUTLS_DHE_PSK_SHA_3DES_EDE_CBC_SHA1, + GNUTLS_CIPHER_3DES_CBC, GNUTLS_KX_DHE_PSK, + GNUTLS_MAC_SHA1, GNUTLS_TLS1), + GNUTLS_CIPHER_SUITE_ENTRY (GNUTLS_DHE_PSK_SHA_AES_128_CBC_SHA1, + GNUTLS_CIPHER_AES_128_CBC, GNUTLS_KX_DHE_PSK, + GNUTLS_MAC_SHA1, GNUTLS_TLS1), + GNUTLS_CIPHER_SUITE_ENTRY (GNUTLS_DHE_PSK_SHA_AES_256_CBC_SHA1, + GNUTLS_CIPHER_AES_256_CBC, GNUTLS_KX_DHE_PSK, + GNUTLS_MAC_SHA1, GNUTLS_TLS1), + + /* SRP */ + GNUTLS_CIPHER_SUITE_ENTRY (GNUTLS_SRP_SHA_3DES_EDE_CBC_SHA1, + GNUTLS_CIPHER_3DES_CBC, GNUTLS_KX_SRP, + GNUTLS_MAC_SHA1, GNUTLS_TLS1), + GNUTLS_CIPHER_SUITE_ENTRY (GNUTLS_SRP_SHA_AES_128_CBC_SHA1, + GNUTLS_CIPHER_AES_128_CBC, GNUTLS_KX_SRP, + GNUTLS_MAC_SHA1, GNUTLS_TLS1), + GNUTLS_CIPHER_SUITE_ENTRY (GNUTLS_SRP_SHA_AES_256_CBC_SHA1, + GNUTLS_CIPHER_AES_256_CBC, GNUTLS_KX_SRP, + GNUTLS_MAC_SHA1, GNUTLS_TLS1), + + GNUTLS_CIPHER_SUITE_ENTRY (GNUTLS_SRP_SHA_DSS_3DES_EDE_CBC_SHA1, + GNUTLS_CIPHER_3DES_CBC, GNUTLS_KX_SRP_DSS, + GNUTLS_MAC_SHA1, GNUTLS_TLS1), + + GNUTLS_CIPHER_SUITE_ENTRY (GNUTLS_SRP_SHA_RSA_3DES_EDE_CBC_SHA1, + GNUTLS_CIPHER_3DES_CBC, GNUTLS_KX_SRP_RSA, + GNUTLS_MAC_SHA1, GNUTLS_TLS1), + + GNUTLS_CIPHER_SUITE_ENTRY (GNUTLS_SRP_SHA_DSS_AES_128_CBC_SHA1, + GNUTLS_CIPHER_AES_128_CBC, GNUTLS_KX_SRP_DSS, + GNUTLS_MAC_SHA1, GNUTLS_TLS1), + + GNUTLS_CIPHER_SUITE_ENTRY (GNUTLS_SRP_SHA_RSA_AES_128_CBC_SHA1, + GNUTLS_CIPHER_AES_128_CBC, GNUTLS_KX_SRP_RSA, + GNUTLS_MAC_SHA1, GNUTLS_TLS1), + + GNUTLS_CIPHER_SUITE_ENTRY (GNUTLS_SRP_SHA_DSS_AES_256_CBC_SHA1, + GNUTLS_CIPHER_AES_256_CBC, GNUTLS_KX_SRP_DSS, + GNUTLS_MAC_SHA1, GNUTLS_TLS1), + + GNUTLS_CIPHER_SUITE_ENTRY (GNUTLS_SRP_SHA_RSA_AES_256_CBC_SHA1, + GNUTLS_CIPHER_AES_256_CBC, GNUTLS_KX_SRP_RSA, + GNUTLS_MAC_SHA1, GNUTLS_TLS1), + + /* DHE_DSS */ + GNUTLS_CIPHER_SUITE_ENTRY (GNUTLS_DHE_DSS_ARCFOUR_SHA1, + GNUTLS_CIPHER_ARCFOUR_128, GNUTLS_KX_DHE_DSS, + GNUTLS_MAC_SHA1, GNUTLS_TLS1), + GNUTLS_CIPHER_SUITE_ENTRY (GNUTLS_DHE_DSS_3DES_EDE_CBC_SHA1, + GNUTLS_CIPHER_3DES_CBC, GNUTLS_KX_DHE_DSS, + GNUTLS_MAC_SHA1, GNUTLS_SSL3), + GNUTLS_CIPHER_SUITE_ENTRY (GNUTLS_DHE_DSS_AES_128_CBC_SHA1, + GNUTLS_CIPHER_AES_128_CBC, GNUTLS_KX_DHE_DSS, + GNUTLS_MAC_SHA1, GNUTLS_SSL3), + GNUTLS_CIPHER_SUITE_ENTRY (GNUTLS_DHE_DSS_AES_256_CBC_SHA1, + GNUTLS_CIPHER_AES_256_CBC, GNUTLS_KX_DHE_DSS, + GNUTLS_MAC_SHA1, GNUTLS_SSL3), +#ifdef ENABLE_CAMELLIA + GNUTLS_CIPHER_SUITE_ENTRY (GNUTLS_DHE_DSS_CAMELLIA_128_CBC_SHA1, + GNUTLS_CIPHER_CAMELLIA_128_CBC, + GNUTLS_KX_DHE_DSS, + GNUTLS_MAC_SHA1, GNUTLS_TLS1), + GNUTLS_CIPHER_SUITE_ENTRY (GNUTLS_DHE_DSS_CAMELLIA_256_CBC_SHA1, + GNUTLS_CIPHER_CAMELLIA_256_CBC, + GNUTLS_KX_DHE_DSS, + GNUTLS_MAC_SHA1, GNUTLS_TLS1), +#endif + /* DHE_RSA */ + GNUTLS_CIPHER_SUITE_ENTRY (GNUTLS_DHE_RSA_3DES_EDE_CBC_SHA1, + GNUTLS_CIPHER_3DES_CBC, GNUTLS_KX_DHE_RSA, + GNUTLS_MAC_SHA1, GNUTLS_SSL3), + GNUTLS_CIPHER_SUITE_ENTRY (GNUTLS_DHE_RSA_AES_128_CBC_SHA1, + GNUTLS_CIPHER_AES_128_CBC, GNUTLS_KX_DHE_RSA, + GNUTLS_MAC_SHA1, GNUTLS_SSL3), + GNUTLS_CIPHER_SUITE_ENTRY (GNUTLS_DHE_RSA_AES_256_CBC_SHA1, + GNUTLS_CIPHER_AES_256_CBC, GNUTLS_KX_DHE_RSA, + GNUTLS_MAC_SHA1, GNUTLS_SSL3), +#ifdef ENABLE_CAMELLIA + GNUTLS_CIPHER_SUITE_ENTRY (GNUTLS_DHE_RSA_CAMELLIA_128_CBC_SHA1, + GNUTLS_CIPHER_CAMELLIA_128_CBC, + GNUTLS_KX_DHE_RSA, + GNUTLS_MAC_SHA1, GNUTLS_TLS1), + GNUTLS_CIPHER_SUITE_ENTRY (GNUTLS_DHE_RSA_CAMELLIA_256_CBC_SHA1, + GNUTLS_CIPHER_CAMELLIA_256_CBC, + GNUTLS_KX_DHE_RSA, + GNUTLS_MAC_SHA1, GNUTLS_TLS1), +#endif + /* RSA */ + GNUTLS_CIPHER_SUITE_ENTRY (GNUTLS_RSA_NULL_MD5, + GNUTLS_CIPHER_NULL, + GNUTLS_KX_RSA, GNUTLS_MAC_MD5, GNUTLS_SSL3), + + GNUTLS_CIPHER_SUITE_ENTRY (GNUTLS_RSA_EXPORT_ARCFOUR_40_MD5, + GNUTLS_CIPHER_ARCFOUR_40, + GNUTLS_KX_RSA_EXPORT, GNUTLS_MAC_MD5, + GNUTLS_SSL3), + + GNUTLS_CIPHER_SUITE_ENTRY (GNUTLS_RSA_ARCFOUR_SHA1, + GNUTLS_CIPHER_ARCFOUR_128, + GNUTLS_KX_RSA, GNUTLS_MAC_SHA1, GNUTLS_SSL3), + GNUTLS_CIPHER_SUITE_ENTRY (GNUTLS_RSA_ARCFOUR_MD5, + GNUTLS_CIPHER_ARCFOUR_128, + GNUTLS_KX_RSA, GNUTLS_MAC_MD5, GNUTLS_SSL3), + GNUTLS_CIPHER_SUITE_ENTRY (GNUTLS_RSA_3DES_EDE_CBC_SHA1, + GNUTLS_CIPHER_3DES_CBC, + GNUTLS_KX_RSA, GNUTLS_MAC_SHA1, GNUTLS_SSL3), + GNUTLS_CIPHER_SUITE_ENTRY (GNUTLS_RSA_AES_128_CBC_SHA1, + GNUTLS_CIPHER_AES_128_CBC, GNUTLS_KX_RSA, + GNUTLS_MAC_SHA1, GNUTLS_SSL3), + GNUTLS_CIPHER_SUITE_ENTRY (GNUTLS_RSA_AES_256_CBC_SHA1, + GNUTLS_CIPHER_AES_256_CBC, GNUTLS_KX_RSA, + GNUTLS_MAC_SHA1, GNUTLS_SSL3), +#ifdef ENABLE_CAMELLIA + GNUTLS_CIPHER_SUITE_ENTRY (GNUTLS_RSA_CAMELLIA_128_CBC_SHA1, + GNUTLS_CIPHER_CAMELLIA_128_CBC, GNUTLS_KX_RSA, + GNUTLS_MAC_SHA1, GNUTLS_TLS1), + GNUTLS_CIPHER_SUITE_ENTRY (GNUTLS_RSA_CAMELLIA_256_CBC_SHA1, + GNUTLS_CIPHER_CAMELLIA_256_CBC, GNUTLS_KX_RSA, + GNUTLS_MAC_SHA1, GNUTLS_TLS1), +#endif + {0, + { + {0, + 0}}, + 0, + 0, + 0, + 0} +}; + +#define GNUTLS_CIPHER_SUITE_LOOP(b) \ + const gnutls_cipher_suite_entry *p; \ + for(p = cs_algorithms; p->name != NULL; p++) { b ; } + +#define GNUTLS_CIPHER_SUITE_ALG_LOOP(a) \ + GNUTLS_CIPHER_SUITE_LOOP( if( (p->id.suite[0] == suite->suite[0]) && (p->id.suite[1] == suite->suite[1])) { a; break; } ) + +/* Generic Functions */ + +int +_gnutls_mac_priority (gnutls_session_t session, + gnutls_mac_algorithm_t algorithm) +{ /* actually returns the priority */ + unsigned int i; + for (i = 0; i < session->internals.priorities.mac.algorithms; i++) + { + if (session->internals.priorities.mac.priority[i] == algorithm) + return i; + } + return -1; +} + +/** + * gnutls_mac_get_name - Returns a string with the name of the specified mac algorithm + * @algorithm: is a MAC algorithm + * + * Returns: a string that contains the name of the specified MAC + * algorithm, or %NULL. + **/ +const char * +gnutls_mac_get_name (gnutls_mac_algorithm_t algorithm) +{ + const char *ret = NULL; + + /* avoid prefix */ + GNUTLS_HASH_ALG_LOOP (ret = p->name); + + return ret; +} + +/** + * gnutls_mac_get_id - Returns the gnutls id of the specified in string algorithm + * @algorithm: is a MAC algorithm name + * + * Returns: an %gnutls_mac_algorithm_tid of the specified in a string + * MAC algorithm, or %GNUTLS_MAC_UNKNOWN on failures. The names are + * compared in a case insensitive way. + **/ +gnutls_mac_algorithm_t +gnutls_mac_get_id (const char *name) +{ + gnutls_mac_algorithm_t ret = GNUTLS_MAC_UNKNOWN; + + GNUTLS_HASH_LOOP (if (strcasecmp (p->name, name) == 0) ret = p->id) + ; + + return ret; +} + +/** + * gnutls_mac_get_key_size - Returns the length of the MAC's key size + * @algorithm: is an encryption algorithm + * + * Returns: length (in bytes) of the given MAC key size, or 0 if the + * given MAC algorithm is invalid. + * + **/ +size_t +gnutls_mac_get_key_size (gnutls_mac_algorithm_t algorithm) +{ + size_t ret = 0; + + /* avoid prefix */ + GNUTLS_HASH_ALG_LOOP (ret = p->key_size); + + return ret; +} + +/** + * gnutls_mac_list: + * + * Get a list of hash algorithms for use as MACs. Note that not + * necessarily all MACs are supported in TLS cipher suites. For + * example, MD2 is not supported as a cipher suite, but is supported + * for other purposes (e.g., X.509 signature verification or similar). + * + * Returns: Return a zero-terminated list of %gnutls_mac_algorithm_t + * integers indicating the available MACs. + **/ +const gnutls_mac_algorithm_t * +gnutls_mac_list (void) +{ + return supported_macs; +} + +const char * +_gnutls_x509_mac_to_oid (gnutls_mac_algorithm_t algorithm) +{ + const char *ret = NULL; + + /* avoid prefix */ + GNUTLS_HASH_ALG_LOOP (ret = p->oid); + + return ret; +} + +gnutls_mac_algorithm_t +_gnutls_x509_oid2mac_algorithm (const char *oid) +{ + gnutls_mac_algorithm_t ret = 0; + + GNUTLS_HASH_LOOP (if (p->oid && strcmp (oid, p->oid) == 0) + { + ret = p->id; break;} + ) + ; + + if (ret == 0) + return GNUTLS_MAC_UNKNOWN; + return ret; +} + +int +_gnutls_mac_is_ok (gnutls_mac_algorithm_t algorithm) +{ + ssize_t ret = -1; + GNUTLS_HASH_ALG_LOOP (ret = p->id); + if (ret >= 0) + ret = 0; + else + ret = 1; + return ret; +} + +/* Compression Functions */ +int +_gnutls_compression_priority (gnutls_session_t session, + gnutls_compression_method_t algorithm) +{ /* actually returns the priority */ + unsigned int i; + for (i = 0; i < session->internals.priorities.compression.algorithms; i++) + { + if (session->internals.priorities.compression.priority[i] == algorithm) + return i; + } + return -1; +} + +/** + * gnutls_compression_get_name - Returns a string with the name of the specified compression algorithm + * @algorithm: is a Compression algorithm + * + * Returns: a pointer to a string that contains the name of the + * specified compression algorithm, or %NULL. + **/ +const char * +gnutls_compression_get_name (gnutls_compression_method_t algorithm) +{ + const char *ret = NULL; + + /* avoid prefix */ + GNUTLS_COMPRESSION_ALG_LOOP (ret = p->name + sizeof ("GNUTLS_COMP_") - 1); + + return ret; +} + +/** + * gnutls_compression_get_id - Returns the gnutls id of the specified in string algorithm + * @algorithm: is a compression method name + * + * The names are compared in a case insensitive way. + * + * Returns: an id of the specified in a string compression method, or + * %GNUTLS_COMP_UNKNOWN on error. + * + **/ +gnutls_compression_method_t +gnutls_compression_get_id (const char *name) +{ + gnutls_compression_method_t ret = GNUTLS_COMP_UNKNOWN; + + GNUTLS_COMPRESSION_LOOP (if + (strcasecmp + (p->name + sizeof ("GNUTLS_COMP_") - 1, + name) == 0) ret = p->id) + ; + + return ret; +} + +/** + * gnutls_compression_list: + * + * Get a list of compression methods. Note that to be able to use LZO + * compression, you must link to libgnutls-extra and call + * gnutls_global_init_extra(). + * + * Returns: a zero-terminated list of %gnutls_compression_method_t + * integers indicating the available compression methods. + **/ +const gnutls_compression_method_t * +gnutls_compression_list (void) +{ + return supported_compressions; +} + +/* return the tls number of the specified algorithm */ +int +_gnutls_compression_get_num (gnutls_compression_method_t algorithm) +{ + int ret = -1; + + /* avoid prefix */ + GNUTLS_COMPRESSION_ALG_LOOP (ret = p->num); + + return ret; +} + +int +_gnutls_compression_get_wbits (gnutls_compression_method_t algorithm) +{ + int ret = -1; + /* avoid prefix */ + GNUTLS_COMPRESSION_ALG_LOOP (ret = p->window_bits); + return ret; +} + +int +_gnutls_compression_get_mem_level (gnutls_compression_method_t algorithm) +{ + int ret = -1; + /* avoid prefix */ + GNUTLS_COMPRESSION_ALG_LOOP (ret = p->mem_level); + return ret; +} + +int +_gnutls_compression_get_comp_level (gnutls_compression_method_t algorithm) +{ + int ret = -1; + /* avoid prefix */ + GNUTLS_COMPRESSION_ALG_LOOP (ret = p->comp_level); + return ret; +} + +/* returns the gnutls internal ID of the TLS compression + * method num + */ +gnutls_compression_method_t +_gnutls_compression_get_id (int num) +{ + gnutls_compression_method_t ret = -1; + + /* avoid prefix */ + GNUTLS_COMPRESSION_ALG_LOOP_NUM (ret = p->id); + + return ret; +} + +int +_gnutls_compression_is_ok (gnutls_compression_method_t algorithm) +{ + ssize_t ret = -1; + GNUTLS_COMPRESSION_ALG_LOOP (ret = p->id); + if (ret >= 0) + ret = 0; + else + ret = 1; + return ret; +} + +/* CIPHER functions */ +int +_gnutls_cipher_get_block_size (gnutls_cipher_algorithm_t algorithm) +{ + size_t ret = 0; + GNUTLS_ALG_LOOP (ret = p->blocksize); + return ret; + +} + +/* returns the priority */ +int +_gnutls_cipher_priority (gnutls_session_t session, + gnutls_cipher_algorithm_t algorithm) +{ + unsigned int i; + for (i = 0; i < session->internals.priorities.cipher.algorithms; i++) + { + if (session->internals.priorities.cipher.priority[i] == algorithm) + return i; + } + return -1; +} + +int +_gnutls_cipher_is_block (gnutls_cipher_algorithm_t algorithm) +{ + size_t ret = 0; + + GNUTLS_ALG_LOOP (ret = p->block); + return ret; + +} + +/** + * gnutls_cipher_get_key_size - Returns the length of the cipher's key size + * @algorithm: is an encryption algorithm + * + * Returns: length (in bytes) of the given cipher's key size, o 0 if + * the given cipher is invalid. + **/ +size_t +gnutls_cipher_get_key_size (gnutls_cipher_algorithm_t algorithm) +{ /* In bytes */ + size_t ret = 0; + GNUTLS_ALG_LOOP (ret = p->keysize); + return ret; + +} + +int +_gnutls_cipher_get_iv_size (gnutls_cipher_algorithm_t algorithm) +{ /* In bytes */ + size_t ret = 0; + GNUTLS_ALG_LOOP (ret = p->iv); + return ret; + +} + +int +_gnutls_cipher_get_export_flag (gnutls_cipher_algorithm_t algorithm) +{ /* In bytes */ + size_t ret = 0; + GNUTLS_ALG_LOOP (ret = p->export_flag); + return ret; + +} + +/** + * gnutls_cipher_get_name - Returns a string with the name of the specified cipher algorithm + * @algorithm: is an encryption algorithm + * + * Returns: a pointer to a string that contains the name of the + * specified cipher, or %NULL. + **/ +const char * +gnutls_cipher_get_name (gnutls_cipher_algorithm_t algorithm) +{ + const char *ret = NULL; + + /* avoid prefix */ + GNUTLS_ALG_LOOP (ret = p->name); + + return ret; +} + +/** + * gnutls_cipher_get_id - Returns the gnutls id of the specified in string algorithm + * @algorithm: is a MAC algorithm name + * + * The names are compared in a case insensitive way. + * + * Returns: an id of the specified cipher, or %GNUTLS_CIPHER_UNKNOWN + * on error. + * + **/ +gnutls_cipher_algorithm_t +gnutls_cipher_get_id (const char *name) +{ + gnutls_cipher_algorithm_t ret = GNUTLS_CIPHER_UNKNOWN; + + GNUTLS_LOOP (if (strcasecmp (p->name, name) == 0) ret = p->id) + ; + + return ret; +} + +/** + * gnutls_cipher_list: + * + * Get a list of supported cipher algorithms. Note that not + * necessarily all ciphers are supported as TLS cipher suites. For + * example, DES is not supported as a cipher suite, but is supported + * for other purposes (e.g., PKCS#8 or similar). + * + * Returns: a zero-terminated list of %gnutls_cipher_algorithm_t + * integers indicating the available ciphers. + * + **/ +const gnutls_cipher_algorithm_t * +gnutls_cipher_list (void) +{ + return supported_ciphers; +} + +int +_gnutls_cipher_is_ok (gnutls_cipher_algorithm_t algorithm) +{ + ssize_t ret = -1; + GNUTLS_ALG_LOOP (ret = p->id); + if (ret >= 0) + ret = 0; + else + ret = 1; + return ret; +} + +/* Key EXCHANGE functions */ +mod_auth_st * +_gnutls_kx_auth_struct (gnutls_kx_algorithm_t algorithm) +{ + mod_auth_st *ret = NULL; + GNUTLS_KX_ALG_LOOP (ret = p->auth_struct); + return ret; + +} + +int +_gnutls_kx_priority (gnutls_session_t session, + gnutls_kx_algorithm_t algorithm) +{ + unsigned int i; + for (i = 0; i < session->internals.priorities.kx.algorithms; i++) + { + if (session->internals.priorities.kx.priority[i] == algorithm) + return i; + } + return -1; +} + +/** + * gnutls_kx_get_name - Returns a string with the name of the specified key exchange algorithm + * @algorithm: is a key exchange algorithm + * + * Returns: a pointer to a string that contains the name of the + * specified key exchange algorithm, or %NULL. + **/ +const char * +gnutls_kx_get_name (gnutls_kx_algorithm_t algorithm) +{ + const char *ret = NULL; + + /* avoid prefix */ + GNUTLS_KX_ALG_LOOP (ret = p->name); + + return ret; +} + +/** + * gnutls_kx_get_id - Returns the gnutls id of the specified in string algorithm + * @algorithm: is a KX name + * + * The names are compared in a case insensitive way. + * + * Returns: an id of the specified KX algorithm, or + * %GNUTLS_KX_UNKNOWN on error. + **/ +gnutls_kx_algorithm_t +gnutls_kx_get_id (const char *name) +{ + gnutls_cipher_algorithm_t ret = GNUTLS_KX_UNKNOWN; + + GNUTLS_KX_LOOP (if (strcasecmp (p->name, name) == 0) ret = p->algorithm) + ; + + return ret; +} + +/** + * gnutls_kx_list: + * + * Get a list of supported key exchange algorithms. + * + * Returns: a zero-terminated list of %gnutls_kx_algorithm_t integers + * indicating the available key exchange algorithms. + **/ +const gnutls_kx_algorithm_t * +gnutls_kx_list (void) +{ + return supported_kxs; +} + +int +_gnutls_kx_is_ok (gnutls_kx_algorithm_t algorithm) +{ + ssize_t ret = -1; + GNUTLS_KX_ALG_LOOP (ret = p->algorithm); + if (ret >= 0) + ret = 0; + else + ret = 1; + return ret; +} + +int +_gnutls_kx_needs_rsa_params (gnutls_kx_algorithm_t algorithm) +{ + ssize_t ret = 0; + GNUTLS_KX_ALG_LOOP (ret = p->needs_rsa_params); + return ret; +} + +int +_gnutls_kx_needs_dh_params (gnutls_kx_algorithm_t algorithm) +{ + ssize_t ret = 0; + GNUTLS_KX_ALG_LOOP (ret = p->needs_dh_params); + return ret; +} + +/* Version */ +int +_gnutls_version_priority (gnutls_session_t session, gnutls_protocol_t version) +{ /* actually returns the priority */ + unsigned int i; + + if (session->internals.priorities.protocol.priority == NULL) + { + gnutls_assert (); + return -1; + } + + for (i = 0; i < session->internals.priorities.protocol.algorithms; i++) + { + if (session->internals.priorities.protocol.priority[i] == version) + return i; + } + return -1; +} + +gnutls_protocol_t +_gnutls_version_lowest (gnutls_session_t session) +{ /* returns the lowest version supported */ + unsigned int i, min = 0xff; + + if (session->internals.priorities.protocol.priority == NULL) + { + return GNUTLS_VERSION_UNKNOWN; + } + else + for (i = 0; i < session->internals.priorities.protocol.algorithms; i++) + { + if (session->internals.priorities.protocol.priority[i] < min) + min = session->internals.priorities.protocol.priority[i]; + } + + if (min == 0xff) + return GNUTLS_VERSION_UNKNOWN; /* unknown version */ + + return min; +} + +gnutls_protocol_t +_gnutls_version_max (gnutls_session_t session) +{ /* returns the maximum version supported */ + unsigned int i, max = 0x00; + + if (session->internals.priorities.protocol.priority == NULL) + { + return GNUTLS_VERSION_UNKNOWN; + } + else + for (i = 0; i < session->internals.priorities.protocol.algorithms; i++) + { + if (session->internals.priorities.protocol.priority[i] > max) + max = session->internals.priorities.protocol.priority[i]; + } + + if (max == 0x00) + return GNUTLS_VERSION_UNKNOWN; /* unknown version */ + + return max; +} + +/** + * gnutls_protocol_get_name - Returns a string with the name of the specified SSL/TLS version + * @version: is a (gnutls) version number + * + * Returns: a string that contains the name of the specified TLS + * version (e.g., "TLS 1.0"), or %NULL. + **/ +const char * +gnutls_protocol_get_name (gnutls_protocol_t version) +{ + const char *ret = NULL; + + /* avoid prefix */ + GNUTLS_VERSION_ALG_LOOP (ret = p->name); + return ret; +} + +/** + * gnutls_protocol_get_id - Returns the gnutls id of the specified in string protocol + * @algorithm: is a protocol name + * + * The names are compared in a case insensitive way. + * + * Returns: an id of the specified protocol, or + * %GNUTLS_VERSION_UNKNOWN on error. + **/ +gnutls_protocol_t +gnutls_protocol_get_id (const char *name) +{ + gnutls_protocol_t ret = GNUTLS_VERSION_UNKNOWN; + + GNUTLS_VERSION_LOOP (if (strcasecmp (p->name, name) == 0) ret = p->id) + ; + + return ret; +} + +/** + * gnutls_protocol_list: + * + * Get a list of supported protocols, e.g. SSL 3.0, TLS 1.0 etc. + * + * Returns: a zero-terminated list of %gnutls_protocol_t integers + * indicating the available protocols. + * + **/ +const gnutls_protocol_t * +gnutls_protocol_list (void) +{ + return supported_protocols; +} + +int +_gnutls_version_get_minor (gnutls_protocol_t version) +{ + int ret = -1; + + GNUTLS_VERSION_ALG_LOOP (ret = p->minor); + return ret; +} + +gnutls_protocol_t +_gnutls_version_get (int major, int minor) +{ + int ret = -1; + + GNUTLS_VERSION_LOOP (if ((p->major == major) && (p->minor == minor)) + ret = p->id) + ; + return ret; +} + +int +_gnutls_version_get_major (gnutls_protocol_t version) +{ + int ret = -1; + + GNUTLS_VERSION_ALG_LOOP (ret = p->major); + return ret; +} + +/* Version Functions */ + +int +_gnutls_version_is_supported (gnutls_session_t session, + const gnutls_protocol_t version) +{ + int ret = 0; + + GNUTLS_VERSION_ALG_LOOP (ret = p->supported); + if (ret == 0) + return 0; + + if (_gnutls_version_priority (session, version) < 0) + return 0; /* disabled by the user */ + else + return 1; +} + +/* Type to KX mappings */ +gnutls_kx_algorithm_t +_gnutls_map_kx_get_kx (gnutls_credentials_type_t type, int server) +{ + gnutls_kx_algorithm_t ret = -1; + + if (server) + { + GNUTLS_KX_MAP_ALG_LOOP_SERVER (ret = p->algorithm); + } + else + { + GNUTLS_KX_MAP_ALG_LOOP_SERVER (ret = p->algorithm); + } + return ret; +} + +gnutls_credentials_type_t +_gnutls_map_kx_get_cred (gnutls_kx_algorithm_t algorithm, int server) +{ + gnutls_credentials_type_t ret = -1; + if (server) + { + GNUTLS_KX_MAP_LOOP (if (p->algorithm == algorithm) ret = p->server_type) + ; + } + else + { + GNUTLS_KX_MAP_LOOP (if (p->algorithm == algorithm) ret = p->client_type) + ; + } + + return ret; +} + +/* Cipher Suite's functions */ +gnutls_cipher_algorithm_t +_gnutls_cipher_suite_get_cipher_algo (const cipher_suite_st * suite) +{ + int ret = 0; + GNUTLS_CIPHER_SUITE_ALG_LOOP (ret = p->block_algorithm); + return ret; +} + +gnutls_protocol_t +_gnutls_cipher_suite_get_version (const cipher_suite_st * suite) +{ + int ret = 0; + GNUTLS_CIPHER_SUITE_ALG_LOOP (ret = p->version); + return ret; +} + +gnutls_kx_algorithm_t +_gnutls_cipher_suite_get_kx_algo (const cipher_suite_st * suite) +{ + int ret = 0; + + GNUTLS_CIPHER_SUITE_ALG_LOOP (ret = p->kx_algorithm); + return ret; + +} + +gnutls_mac_algorithm_t +_gnutls_cipher_suite_get_mac_algo (const cipher_suite_st * suite) +{ /* In bytes */ + int ret = 0; + GNUTLS_CIPHER_SUITE_ALG_LOOP (ret = p->mac_algorithm); + return ret; + +} + +const char * +_gnutls_cipher_suite_get_name (cipher_suite_st * suite) +{ + const char *ret = NULL; + + /* avoid prefix */ + GNUTLS_CIPHER_SUITE_ALG_LOOP (ret = p->name + sizeof ("GNUTLS_") - 1); + + return ret; +} + +/** + * gnutls_cipher_suite_get_name - Returns a string with the name of the specified cipher suite + * @kx_algorithm: is a Key exchange algorithm + * @cipher_algorithm: is a cipher algorithm + * @mac_algorithm: is a MAC algorithm + * + * Note that the full cipher suite name must be prepended by TLS or + * SSL depending of the protocol in use. + * + * Returns: a string that contains the name of a TLS cipher suite, + * specified by the given algorithms, or %NULL. + **/ +const char * +gnutls_cipher_suite_get_name (gnutls_kx_algorithm_t kx_algorithm, + gnutls_cipher_algorithm_t cipher_algorithm, + gnutls_mac_algorithm_t mac_algorithm) +{ + const char *ret = NULL; + + /* avoid prefix */ + GNUTLS_CIPHER_SUITE_LOOP (if (kx_algorithm == p->kx_algorithm && + cipher_algorithm == p->block_algorithm && + mac_algorithm == p->mac_algorithm) + ret = p->name + sizeof ("GNUTLS_") - 1) + ; + + return ret; +} + +/** + * gnutls_cipher_suite_info: + * @idx: index of cipher suite to get information about, starts on 0. + * @cs_id: output buffer with room for 2 bytes, indicating cipher suite value + * @kx: output variable indicating key exchange algorithm, or %NULL. + * @cipher: output variable indicating cipher, or %NULL. + * @mac: output variable indicating MAC algorithm, or %NULL. + * @version: output variable indicating TLS protocol version, or %NULL. + * + * Get information about supported cipher suites. Use the function + * iteratively to get information about all supported cipher suites. + * Call with idx=0 to get information about first cipher suite, then + * idx=1 and so on until the function returns NULL. + * + * Returns: the name of @idx cipher suite, and set the information + * about the cipher suite in the output variables. If @idx is out of + * bounds, %NULL is returned. + **/ +const char * +gnutls_cipher_suite_info (size_t idx, + char *cs_id, + gnutls_kx_algorithm_t * kx, + gnutls_cipher_algorithm_t * cipher, + gnutls_mac_algorithm_t * mac, + gnutls_protocol_t * version) +{ + if (idx >= CIPHER_SUITES_COUNT) + return NULL; + + if (cs_id) + memcpy (cs_id, cs_algorithms[idx].id.suite, 2); + if (kx) + *kx = cs_algorithms[idx].kx_algorithm; + if (cipher) + *cipher = cs_algorithms[idx].block_algorithm; + if (mac) + *mac = cs_algorithms[idx].mac_algorithm; + if (version) + *version = cs_algorithms[idx].version; + + return cs_algorithms[idx].name + sizeof ("GNU") - 1; +} + +static inline int +_gnutls_cipher_suite_is_ok (cipher_suite_st * suite) +{ + size_t ret; + const char *name = NULL; + + GNUTLS_CIPHER_SUITE_ALG_LOOP (name = p->name); + if (name != NULL) + ret = 0; + else + ret = 1; + return ret; + +} + +#define SWAP(x, y) memcpy(tmp,x,size); \ + memcpy(x,y,size); \ + memcpy(y,tmp,size); + +#define MAX_ELEM_SIZE 4 +static inline int +_gnutls_partition (gnutls_session_t session, + void *_base, + size_t nmemb, + size_t size, + int (*compar) (gnutls_session_t, + const void *, const void *)) +{ + uint8_t *base = _base; + uint8_t tmp[MAX_ELEM_SIZE]; + uint8_t ptmp[MAX_ELEM_SIZE]; + unsigned int pivot; + unsigned int i, j; + unsigned int full; + + i = pivot = 0; + j = full = (nmemb - 1) * size; + + memcpy (ptmp, &base[0], size); /* set pivot item */ + + while (i < j) + { + while ((compar (session, &base[i], ptmp) <= 0) && (i < full)) + { + i += size; + } + while ((compar (session, &base[j], ptmp) >= 0) && (j > 0)) + j -= size; + + if (i < j) + { + SWAP (&base[j], &base[i]); + } + } + + if (j > pivot) + { + SWAP (&base[pivot], &base[j]); + pivot = j; + } + else if (i < pivot) + { + SWAP (&base[pivot], &base[i]); + pivot = i; + } + return pivot / size; +} + +static void +_gnutls_qsort (gnutls_session_t session, + void *_base, + size_t nmemb, + size_t size, + int (*compar) (gnutls_session_t, const void *, const void *)) +{ + unsigned int pivot; + char *base = _base; + size_t snmemb = nmemb; + +#ifdef DEBUG + if (size > MAX_ELEM_SIZE) + { + gnutls_assert (); + _gnutls_debug_log ("QSORT BUG\n"); + exit (1); + } +#endif + + if (snmemb <= 1) + return; + pivot = _gnutls_partition (session, _base, nmemb, size, compar); + + _gnutls_qsort (session, base, pivot < nmemb ? pivot + 1 + : pivot, size, compar); + _gnutls_qsort (session, &base[(pivot + 1) * size], nmemb - pivot - 1, size, + compar); +} + +/* a compare function for KX algorithms (using priorities). + * For use with qsort + */ +static int +_gnutls_compare_algo (gnutls_session_t session, + const void *i_A1, const void *i_A2) +{ + gnutls_kx_algorithm_t kA1 = + _gnutls_cipher_suite_get_kx_algo ((const cipher_suite_st *) i_A1); + gnutls_kx_algorithm_t kA2 = + _gnutls_cipher_suite_get_kx_algo ((const cipher_suite_st *) i_A2); + gnutls_cipher_algorithm_t cA1 = + _gnutls_cipher_suite_get_cipher_algo ((const cipher_suite_st *) i_A1); + gnutls_cipher_algorithm_t cA2 = + _gnutls_cipher_suite_get_cipher_algo ((const cipher_suite_st *) i_A2); + gnutls_mac_algorithm_t mA1 = + _gnutls_cipher_suite_get_mac_algo ((const cipher_suite_st *) i_A1); + gnutls_mac_algorithm_t mA2 = + _gnutls_cipher_suite_get_mac_algo ((const cipher_suite_st *) i_A2); + + int p1 = (_gnutls_kx_priority (session, kA1) + 1) * 64; + int p2 = (_gnutls_kx_priority (session, kA2) + 1) * 64; + p1 += (_gnutls_cipher_priority (session, cA1) + 1) * 8; + p2 += (_gnutls_cipher_priority (session, cA2) + 1) * 8; + p1 += _gnutls_mac_priority (session, mA1); + p2 += _gnutls_mac_priority (session, mA2); + + if (p1 > p2) + { + return 1; + } + else + { + if (p1 == p2) + { + return 0; + } + return -1; + } +} + +#ifdef SORT_DEBUG +static void +_gnutls_bsort (gnutls_session_t session, void *_base, size_t nmemb, + size_t size, int (*compar) (gnutls_session_t, const void *, + const void *)) +{ + unsigned int i, j; + int full = nmemb * size; + char *base = _base; + char tmp[MAX_ELEM_SIZE]; + + for (i = 0; i < full; i += size) + { + for (j = 0; j < full; j += size) + { + if (compar (session, &base[i], &base[j]) < 0) + { + SWAP (&base[j], &base[i]); + } + } + } + +} +#endif + +int +_gnutls_supported_ciphersuites_sorted (gnutls_session_t session, + cipher_suite_st ** ciphers) +{ + +#ifdef SORT_DEBUG + unsigned int i; +#endif + int count; + + count = _gnutls_supported_ciphersuites (session, ciphers); + if (count <= 0) + { + gnutls_assert (); + return count; + } +#ifdef SORT_DEBUG + _gnutls_debug_log ("Unsorted: \n"); + for (i = 0; i < count; i++) + _gnutls_debug_log ("\t%d: %s\n", i, + _gnutls_cipher_suite_get_name ((*ciphers)[i])); +#endif + + _gnutls_qsort (session, *ciphers, count, sizeof (cipher_suite_st), + _gnutls_compare_algo); + +#ifdef SORT_DEBUG + _gnutls_debug_log ("Sorted: \n"); + for (i = 0; i < count; i++) + _gnutls_debug_log ("\t%d: %s\n", i, + _gnutls_cipher_suite_get_name ((*ciphers)[i])); +#endif + + return count; +} + +int +_gnutls_supported_ciphersuites (gnutls_session_t session, + cipher_suite_st ** _ciphers) +{ + + unsigned int i, ret_count, j; + unsigned int count = CIPHER_SUITES_COUNT; + cipher_suite_st *tmp_ciphers; + cipher_suite_st *ciphers; + gnutls_protocol_t version; + + if (count == 0) + { + return 0; + } + + tmp_ciphers = gnutls_alloca (count * sizeof (cipher_suite_st)); + if (tmp_ciphers == NULL) + return GNUTLS_E_MEMORY_ERROR; + + ciphers = gnutls_malloc (count * sizeof (cipher_suite_st)); + if (ciphers == NULL) + { + gnutls_afree (tmp_ciphers); + return GNUTLS_E_MEMORY_ERROR; + } + + version = gnutls_protocol_get_version (session); + + for (i = 0; i < count; i++) + { + memcpy (&tmp_ciphers[i], &cs_algorithms[i].id, + sizeof (cipher_suite_st)); + } + + for (i = j = 0; i < count; i++) + { + /* remove private cipher suites, if requested. + */ + if (tmp_ciphers[i].suite[0] == 0xFF && session->internals.enable_private + == 0) + continue; + + /* remove cipher suites which do not support the + * protocol version used. + */ + if (_gnutls_cipher_suite_get_version (&tmp_ciphers[i]) > version) + continue; + + if (_gnutls_kx_priority (session, + _gnutls_cipher_suite_get_kx_algo (&tmp_ciphers + [i])) < 0) + continue; + if (_gnutls_mac_priority (session, + _gnutls_cipher_suite_get_mac_algo + (&tmp_ciphers[i])) < 0) + continue; + if (_gnutls_cipher_priority (session, + _gnutls_cipher_suite_get_cipher_algo + (&tmp_ciphers[i])) < 0) + continue; + + memcpy (&ciphers[j], &tmp_ciphers[i], sizeof (cipher_suite_st)); + j++; + } + + ret_count = j; + +#if 0 /* expensive */ + if (ret_count > 0 && ret_count != count) + { + ciphers = + gnutls_realloc_fast (ciphers, ret_count * sizeof (cipher_suite_st)); + } + else + { + if (ret_count != count) + { + gnutls_free (ciphers); + ciphers = NULL; + } + } +#endif + + gnutls_afree (tmp_ciphers); + + /* This function can no longer return 0 cipher suites. + * It returns an error code instead. + */ + if (ret_count == 0) + { + gnutls_assert (); + gnutls_free (ciphers); + return GNUTLS_E_NO_CIPHER_SUITES; + } + *_ciphers = ciphers; + return ret_count; +} + +/* For compression */ + +#define MIN_PRIVATE_COMP_ALGO 0xEF + +/* returns the TLS numbers of the compression methods we support + */ +#define SUPPORTED_COMPRESSION_METHODS session->internals.priorities.compression.algorithms +int +_gnutls_supported_compression_methods (gnutls_session_t session, + uint8_t ** comp) +{ + unsigned int i, j; + + *comp = gnutls_malloc (sizeof (uint8_t) * SUPPORTED_COMPRESSION_METHODS); + if (*comp == NULL) + return GNUTLS_E_MEMORY_ERROR; + + for (i = j = 0; i < SUPPORTED_COMPRESSION_METHODS; i++) + { + int tmp = _gnutls_compression_get_num (session->internals.priorities. + compression.priority[i]); + + /* remove private compression algorithms, if requested. + */ + if (tmp == -1 || (tmp >= MIN_PRIVATE_COMP_ALGO + && session->internals.enable_private == 0)) + { + gnutls_assert (); + continue; + } + + (*comp)[j] = (uint8_t) tmp; + j++; + } + + if (j == 0) + { + gnutls_assert (); + gnutls_free (*comp); + *comp = NULL; + return GNUTLS_E_NO_COMPRESSION_ALGORITHMS; + } + return j; +} + +/** + * gnutls_certificate_type_get_name - Returns a string with the name of the specified certificate type + * @type: is a certificate type + * + * Returns: a string (or %NULL) that contains the name of the + * specified certificate type. + **/ +const char * +gnutls_certificate_type_get_name (gnutls_certificate_type_t type) +{ + const char *ret = NULL; + + if (type == GNUTLS_CRT_X509) + ret = "X.509"; + if (type == GNUTLS_CRT_OPENPGP) + ret = "OPENPGP"; + + return ret; +} + +/** + * gnutls_certificate_type_get_id - Returns the gnutls id of the specified in string type + * @name: is a certificate type name + * + * The names are compared in a case insensitive way. + * + * Returns: an id of the specified in a string certificate type, or + * %GNUTLS_CRT_UNKNOWN on error. + **/ +gnutls_certificate_type_t +gnutls_certificate_type_get_id (const char *name) +{ + gnutls_certificate_type_t ret = GNUTLS_CRT_UNKNOWN; + + if (strcasecmp (name, "X.509") == 0 || strcasecmp (name, "X509") == 0) + return GNUTLS_CRT_X509; + if (strcasecmp (name, "OPENPGP") == 0) + return GNUTLS_CRT_OPENPGP; + + return ret; +} + +static const gnutls_certificate_type_t supported_certificate_types[] = + { GNUTLS_CRT_X509, + GNUTLS_CRT_OPENPGP, + 0 +}; + +/** + * gnutls_certificate_type_list: + * + * Get a list of certificate types. Note that to be able to use + * OpenPGP certificates, you must link to libgnutls-extra and call + * gnutls_global_init_extra(). + * + * Returns: a zero-terminated list of %gnutls_certificate_type_t + * integers indicating the available certificate types. + * + **/ +const gnutls_certificate_type_t * +gnutls_certificate_type_list (void) +{ + return supported_certificate_types; +} + +/* returns the gnutls_pk_algorithm_t which is compatible with + * the given gnutls_kx_algorithm_t. + */ +gnutls_pk_algorithm_t +_gnutls_map_pk_get_pk (gnutls_kx_algorithm_t kx_algorithm) +{ + gnutls_pk_algorithm_t ret = -1; + + GNUTLS_PK_MAP_ALG_LOOP (ret = p->pk_algorithm) return ret; +} + +/* Returns the encipher type for the given key exchange algorithm. + * That one of CIPHER_ENCRYPT, CIPHER_SIGN, CIPHER_IGN. + * + * ex. GNUTLS_KX_RSA requires a certificate able to encrypt... so returns CIPHER_ENCRYPT. + */ +enum encipher_type +_gnutls_kx_encipher_type (gnutls_kx_algorithm_t kx_algorithm) +{ + int ret = CIPHER_IGN; + GNUTLS_PK_MAP_ALG_LOOP (ret = p->encipher_type) return ret; + +} + +/* signature algorithms; + */ +struct gnutls_sign_entry +{ + const char *name; + const char *oid; + gnutls_sign_algorithm_t id; + gnutls_pk_algorithm_t pk; + gnutls_mac_algorithm_t mac; +}; +typedef struct gnutls_sign_entry gnutls_sign_entry; + +static const gnutls_sign_entry sign_algorithms[] = { + {"RSA-SHA", + SIG_RSA_SHA1_OID, + GNUTLS_SIGN_RSA_SHA1, + GNUTLS_PK_RSA, + GNUTLS_MAC_SHA1}, + {"RSA-SHA256", + SIG_RSA_SHA256_OID, + GNUTLS_SIGN_RSA_SHA256, + GNUTLS_PK_RSA, + GNUTLS_MAC_SHA256}, + {"RSA-MD5", + SIG_RSA_MD5_OID, + GNUTLS_SIGN_RSA_MD5, + GNUTLS_PK_RSA, + GNUTLS_MAC_MD5}, + {"GOST R 34.10-2001", + SIG_GOST_R3410_2001_OID, + 0, + 0, + 0}, + {"GOST R 34.10-94", + SIG_GOST_R3410_94_OID, + 0, + 0, + 0}, + {0, + 0, + 0, + 0, + 0} +}; + +#define GNUTLS_SIGN_LOOP(b) \ + do { \ + const gnutls_sign_entry *p; \ + for(p = sign_algorithms; p->name != NULL; p++) { b ; } \ + } while (0) + +#define GNUTLS_SIGN_ALG_LOOP(a) \ + GNUTLS_SIGN_LOOP( if(p->id && p->id == sign) { a; break; } ) + +/** + * gnutls_sign_algorithm_get_name - Returns a string with the name of the specified sign algorithm + * @algorithm: is a sign algorithm + * + * Returns: a string that contains the name of the specified sign + * algorithm, or %NULL. + **/ +const char * +gnutls_sign_algorithm_get_name (gnutls_sign_algorithm_t sign) +{ + const char *ret = NULL; + + /* avoid prefix */ + GNUTLS_SIGN_ALG_LOOP (ret = p->name); + + return ret; +} + +gnutls_sign_algorithm_t +_gnutls_x509_oid2sign_algorithm (const char *oid) +{ + gnutls_sign_algorithm_t ret = 0; + + GNUTLS_SIGN_LOOP (if (strcmp (oid, p->oid) == 0) + { + ret = p->id; break;} + ); + + if (ret == 0) + { + _gnutls_x509_log ("Unknown SIGN OID: '%s'\n", oid); + return GNUTLS_SIGN_UNKNOWN; + } + return ret; +} + +gnutls_sign_algorithm_t +_gnutls_x509_pk_to_sign (gnutls_pk_algorithm_t pk, gnutls_mac_algorithm_t mac) +{ + gnutls_sign_algorithm_t ret = 0; + + GNUTLS_SIGN_LOOP (if (pk == p->pk && mac == p->mac) + { + ret = p->id; break;} + ); + + if (ret == 0) + return GNUTLS_SIGN_UNKNOWN; + return ret; +} + +const char * +_gnutls_x509_sign_to_oid (gnutls_pk_algorithm_t pk, + gnutls_mac_algorithm_t mac) +{ + gnutls_sign_algorithm_t sign; + const char *ret = NULL; + + sign = _gnutls_x509_pk_to_sign (pk, mac); + if (sign == GNUTLS_SIGN_UNKNOWN) + return NULL; + + GNUTLS_SIGN_ALG_LOOP (ret = p->oid); + return ret; +} + +/* pk algorithms; + */ +struct gnutls_pk_entry +{ + const char *name; + const char *oid; + gnutls_pk_algorithm_t id; +}; +typedef struct gnutls_pk_entry gnutls_pk_entry; + +static const gnutls_pk_entry pk_algorithms[] = { + {"RSA", + PK_PKIX1_RSA_OID, + GNUTLS_PK_RSA}, + {"GOST R 34.10-2001", + PK_GOST_R3410_2001_OID, + 0}, + {"GOST R 34.10-94", + PK_GOST_R3410_94_OID, + 0}, + {0, + 0, + 0} +}; + +/** + * gnutls_pk_algorithm_get_name - Returns a string with the name of the specified public key algorithm + * @algorithm: is a pk algorithm + * + * Returns: a string that contains the name of the specified public + * key algorithm, or %NULL. + **/ +const char * +gnutls_pk_algorithm_get_name (gnutls_pk_algorithm_t algorithm) +{ + const char *ret = NULL; + const gnutls_pk_entry *p; + + for (p = pk_algorithms; p->name != NULL; p++) + if (p->id && p->id == algorithm) + { + ret = p->name; + break; + } + + return ret; +} + +gnutls_pk_algorithm_t +_gnutls_x509_oid2pk_algorithm (const char *oid) +{ + gnutls_pk_algorithm_t ret = GNUTLS_PK_UNKNOWN; + const gnutls_pk_entry *p; + + for (p = pk_algorithms; p->name != NULL; p++) + if (strcmp (p->oid, oid) == 0) + { + ret = p->id; + break; + } + + return ret; +} + +const char * +_gnutls_x509_pk_to_oid (gnutls_pk_algorithm_t algorithm) +{ + const char *ret = NULL; + const gnutls_pk_entry *p; + + for (p = pk_algorithms; p->name != NULL; p++) + if (p->id == algorithm) + { + ret = p->oid; + break; + } + + return ret; +} diff --git a/src/daemon/https/tls/gnutls_algorithms.h b/src/daemon/https/tls/gnutls_algorithms.h new file mode 100644 index 00000000..fd23ef0a --- /dev/null +++ b/src/daemon/https/tls/gnutls_algorithms.h @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2007 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +#ifndef ALGORITHMS_H +# define ALGORITHMS_H + +#include "gnutls_auth.h" + +/* Functions for version handling. */ +gnutls_protocol_t _gnutls_version_lowest (gnutls_session_t session); +gnutls_protocol_t _gnutls_version_max (gnutls_session_t session); +int _gnutls_version_priority (gnutls_session_t session, + gnutls_protocol_t version); +int _gnutls_version_is_supported (gnutls_session_t session, + const gnutls_protocol_t version); +int _gnutls_version_get_major (gnutls_protocol_t ver); +int _gnutls_version_get_minor (gnutls_protocol_t ver); +gnutls_protocol_t _gnutls_version_get (int major, int minor); + +/* Functions for MACs. */ +int _gnutls_mac_is_ok (gnutls_mac_algorithm_t algorithm); +gnutls_mac_algorithm_t _gnutls_x509_oid2mac_algorithm (const char *oid); +const char *_gnutls_x509_mac_to_oid (gnutls_mac_algorithm_t mac); + +/* Functions for cipher suites. */ +int _gnutls_supported_ciphersuites (gnutls_session_t session, + cipher_suite_st ** ciphers); +int _gnutls_supported_ciphersuites_sorted (gnutls_session_t session, + cipher_suite_st ** ciphers); +int _gnutls_supported_compression_methods (gnutls_session_t session, + uint8_t ** comp); +const char *_gnutls_cipher_suite_get_name (cipher_suite_st * algorithm); +gnutls_cipher_algorithm_t _gnutls_cipher_suite_get_cipher_algo (const + cipher_suite_st + * algorithm); +gnutls_kx_algorithm_t _gnutls_cipher_suite_get_kx_algo (const cipher_suite_st + * algorithm); +gnutls_mac_algorithm_t _gnutls_cipher_suite_get_mac_algo (const + cipher_suite_st * + algorithm); +gnutls_protocol_t _gnutls_cipher_suite_get_version (const cipher_suite_st * + algorithm); +cipher_suite_st _gnutls_cipher_suite_get_suite_name (cipher_suite_st * + algorithm); + +/* Functions for ciphers. */ +int _gnutls_cipher_get_block_size (gnutls_cipher_algorithm_t algorithm); +int _gnutls_cipher_is_block (gnutls_cipher_algorithm_t algorithm); +int _gnutls_cipher_is_ok (gnutls_cipher_algorithm_t algorithm); +int _gnutls_cipher_get_iv_size (gnutls_cipher_algorithm_t algorithm); +int _gnutls_cipher_get_export_flag (gnutls_cipher_algorithm_t algorithm); + +/* Functions for key exchange. */ +int _gnutls_kx_needs_dh_params (gnutls_kx_algorithm_t algorithm); +int _gnutls_kx_needs_rsa_params (gnutls_kx_algorithm_t algorithm); +mod_auth_st *_gnutls_kx_auth_struct (gnutls_kx_algorithm_t algorithm); +int _gnutls_kx_is_ok (gnutls_kx_algorithm_t algorithm); + +/* Functions for compression. */ +int _gnutls_compression_is_ok (gnutls_compression_method_t algorithm); +int _gnutls_compression_get_num (gnutls_compression_method_t algorithm); +gnutls_compression_method_t _gnutls_compression_get_id (int num); +int _gnutls_compression_get_mem_level (gnutls_compression_method_t algorithm); +int _gnutls_compression_get_comp_level (gnutls_compression_method_t + algorithm); +int _gnutls_compression_get_wbits (gnutls_compression_method_t algorithm); + +/* Type to KX mappings. */ +gnutls_kx_algorithm_t _gnutls_map_kx_get_kx (gnutls_credentials_type_t type, + int server); +gnutls_credentials_type_t _gnutls_map_kx_get_cred (gnutls_kx_algorithm_t + algorithm, int server); + +/* KX to PK mapping. */ +gnutls_pk_algorithm_t _gnutls_map_pk_get_pk (gnutls_kx_algorithm_t + kx_algorithm); +gnutls_pk_algorithm_t _gnutls_x509_oid2pk_algorithm (const char *oid); +const char *_gnutls_x509_pk_to_oid (gnutls_pk_algorithm_t pk); + +enum encipher_type +{ CIPHER_ENCRYPT = 0, CIPHER_SIGN = 1, CIPHER_IGN }; + +enum encipher_type _gnutls_kx_encipher_type (gnutls_kx_algorithm_t algorithm); + +struct gnutls_compression_entry +{ + const char *name; + gnutls_compression_method_t id; + int num; /* the number reserved in TLS for the specific compression method */ + + /* used in zlib compressor */ + int window_bits; + int mem_level; + int comp_level; +}; +typedef struct gnutls_compression_entry gnutls_compression_entry; + +/* Functions for sign algorithms. */ +gnutls_sign_algorithm_t _gnutls_x509_oid2sign_algorithm (const char *oid); +gnutls_sign_algorithm_t _gnutls_x509_pk_to_sign (gnutls_pk_algorithm_t pk, + gnutls_mac_algorithm_t mac); +const char *_gnutls_x509_sign_to_oid (gnutls_pk_algorithm_t, + gnutls_mac_algorithm_t mac); + +int _gnutls_mac_priority (gnutls_session_t session, + gnutls_mac_algorithm_t algorithm); +int _gnutls_cipher_priority (gnutls_session_t session, + gnutls_cipher_algorithm_t algorithm); +int _gnutls_kx_priority (gnutls_session_t session, + gnutls_kx_algorithm_t algorithm); +int _gnutls_compression_priority (gnutls_session_t session, + gnutls_compression_method_t algorithm); + +gnutls_mac_algorithm_t gnutls_mac_get_id (const char* name); +gnutls_cipher_algorithm_t gnutls_cipher_get_id (const char* name); +gnutls_kx_algorithm_t gnutls_kx_get_id (const char* name); +gnutls_protocol_t gnutls_protocol_get_id (const char* name); +gnutls_certificate_type_t gnutls_certificate_type_get_id (const char* name); + +#endif diff --git a/src/daemon/https/tls/gnutls_anon_cred.c b/src/daemon/https/tls/gnutls_anon_cred.c new file mode 100644 index 00000000..919315ef --- /dev/null +++ b/src/daemon/https/tls/gnutls_anon_cred.c @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2001, 2004, 2005, 2007 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +#include "gnutls_int.h" + +#ifdef ENABLE_ANON + +#include "gnutls_errors.h" +#include "auth_anon.h" +#include "gnutls_auth_int.h" +#include "gnutls_dh.h" +#include "gnutls_num.h" +#include "gnutls_mpi.h" + +static const int anon_dummy; + +/** + * gnutls_anon_free_server_credentials - Used to free an allocated gnutls_anon_server_credentials_t structure + * @sc: is an #gnutls_anon_server_credentials_t structure. + * + * This structure is complex enough to manipulate directly thus this + * helper function is provided in order to free (deallocate) it. + **/ +void +gnutls_anon_free_server_credentials (gnutls_anon_server_credentials_t sc) +{ + + gnutls_free (sc); +} + +/** + * gnutls_anon_allocate_server_credentials - Used to allocate an gnutls_anon_server_credentials_t structure + * @sc: is a pointer to an #gnutls_anon_server_credentials_t structure. + * + * This structure is complex enough to manipulate directly thus this + * helper function is provided in order to allocate it. + * + * Returns: %GNUTLS_E_SUCCESS on success, or an error code. + **/ +int +gnutls_anon_allocate_server_credentials (gnutls_anon_server_credentials_t * + sc) +{ + + *sc = gnutls_calloc (1, sizeof (anon_server_credentials_st)); + + return 0; +} + + +/** + * gnutls_anon_free_client_credentials - Used to free an allocated gnutls_anon_client_credentials_t structure + * @sc: is an #gnutls_anon_client_credentials_t structure. + * + * This structure is complex enough to manipulate directly thus this + * helper function is provided in order to free (deallocate) it. + **/ +void +gnutls_anon_free_client_credentials (gnutls_anon_client_credentials_t sc) +{ +} + +/** + * gnutls_anon_allocate_client_credentials - Used to allocate a credentials structure + * @sc: is a pointer to an #gnutls_anon_client_credentials_t structure. + * + * This structure is complex enough to manipulate directly thus + * this helper function is provided in order to allocate it. + * + * Returns: %GNUTLS_E_SUCCESS on success, or an error code. + **/ +int +gnutls_anon_allocate_client_credentials (gnutls_anon_client_credentials_t * + sc) +{ + /* anon_dummy is only there for *sc not to be null. + * it is not used at all; + */ + *sc = (void *) &anon_dummy; + + return 0; +} + +/** + * gnutls_anon_set_server_dh_params - This function will set the DH parameters for a server to use + * @res: is a gnutls_anon_server_credentials_t structure + * @dh_params: is a structure that holds diffie hellman parameters. + * + * This function will set the diffie hellman parameters for an + * anonymous server to use. These parameters will be used in + * Anonymous Diffie Hellman cipher suites. + **/ +void +gnutls_anon_set_server_dh_params (gnutls_anon_server_credentials_t res, + gnutls_dh_params_t dh_params) +{ + res->dh_params = dh_params; +} + +/** + * gnutls_anon_set_server_params_function - This function will set the DH parameters callback + * @res: is a gnutls_certificate_credentials_t structure + * @func: is the function to be called + * + * This function will set a callback in order for the server to get + * the diffie hellman parameters for anonymous authentication. The + * callback should return zero on success. + **/ +void +gnutls_anon_set_server_params_function (gnutls_anon_server_credentials_t res, + gnutls_params_function * func) +{ + res->params_func = func; +} + +#endif diff --git a/src/daemon/https/tls/gnutls_asn1_tab.c b/src/daemon/https/tls/gnutls_asn1_tab.c new file mode 100644 index 00000000..cabea4e1 --- /dev/null +++ b/src/daemon/https/tls/gnutls_asn1_tab.c @@ -0,0 +1,63 @@ +#if HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +extern const ASN1_ARRAY_TYPE gnutls_asn1_tab[] = { + {"GNUTLS", 536872976, 0}, + {0, 1073741836, 0}, + {"RSAPublicKey", 1610612741, 0}, + {"modulus", 1073741827, 0}, + {"publicExponent", 3, 0}, + {"RSAPrivateKey", 1610612741, 0}, + {"version", 1073741826, "Version"}, + {"modulus", 1073741827, 0}, + {"publicExponent", 1073741827, 0}, + {"privateExponent", 1073741827, 0}, + {"prime1", 1073741827, 0}, + {"prime2", 1073741827, 0}, + {"exponent1", 1073741827, 0}, + {"exponent2", 1073741827, 0}, + {"coefficient", 1073741827, 0}, + {"otherPrimeInfos", 16386, "OtherPrimeInfos"}, + {"Version", 1610874883, 0}, + {"two-prime", 1073741825, "0"}, + {"multi", 1, "1"}, + {"OtherPrimeInfos", 1612709899, 0}, + {"MAX", 1074266122, "1"}, + {0, 2, "OtherPrimeInfo"}, + {"OtherPrimeInfo", 1610612741, 0}, + {"prime", 1073741827, 0}, + {"exponent", 1073741827, 0}, + {"coefficient", 3, 0}, + {"AlgorithmIdentifier", 1610612741, 0}, + {"algorithm", 1073741836, 0}, + {"parameters", 541081613, 0}, + {"algorithm", 1, 0}, + {"DigestInfo", 1610612741, 0}, + {"digestAlgorithm", 1073741826, "DigestAlgorithmIdentifier"}, + {"digest", 2, "Digest"}, + {"DigestAlgorithmIdentifier", 1073741826, "AlgorithmIdentifier"}, + {"Digest", 1073741831, 0}, + {"DSAPublicKey", 1073741827, 0}, + {"DSAParameters", 1610612741, 0}, + {"p", 1073741827, 0}, + {"q", 1073741827, 0}, + {"g", 3, 0}, + {"DSASignatureValue", 1610612741, 0}, + {"r", 1073741827, 0}, + {"s", 3, 0}, + {"DSAPrivateKey", 1610612741, 0}, + {"version", 1073741827, 0}, + {"p", 1073741827, 0}, + {"q", 1073741827, 0}, + {"g", 1073741827, 0}, + {"Y", 1073741827, 0}, + {"priv", 3, 0}, + {"DHParameter", 536870917, 0}, + {"prime", 1073741827, 0}, + {"base", 1073741827, 0}, + {"privateValueLength", 16387, 0}, + {0, 0, 0} +}; diff --git a/src/daemon/https/tls/gnutls_auth.c b/src/daemon/https/tls/gnutls_auth.c new file mode 100644 index 00000000..89640402 --- /dev/null +++ b/src/daemon/https/tls/gnutls_auth.c @@ -0,0 +1,413 @@ +/* + * Copyright (C) 2001, 2002, 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +#include "gnutls_int.h" +#include "gnutls_errors.h" +#include "gnutls_auth.h" +#include "gnutls_auth_int.h" +#include "gnutls_algorithms.h" +#include "auth_cert.h" +#include + +#include "auth_anon.h" +/* The functions here are used in order for authentication algorithms + * to be able to retrieve the needed credentials eg public and private + * key etc. + */ + +/** + * gnutls_credentials_clear - Clears all the credentials previously set + * @session: is a #gnutls_session_t structure. + * + * Clears all the credentials previously set in this session. + * + **/ +void +gnutls_credentials_clear (gnutls_session_t session) +{ + if (session->key && session->key->cred) + { /* beginning of the list */ + auth_cred_st *ccred, *ncred; + ccred = session->key->cred; + while (ccred != NULL) + { + ncred = ccred->next; + gnutls_free (ccred); + ccred = ncred; + } + session->key->cred = NULL; + } +} + +/* + * This creates a linked list of the form: + * { algorithm, credentials, pointer to next } + */ +/** + * gnutls_credentials_set - Sets the needed credentials for the specified authentication algorithm. + * @session: is a #gnutls_session_t structure. + * @type: is the type of the credentials + * @cred: is a pointer to a structure. + * + * Sets the needed credentials for the specified type. + * Eg username, password - or public and private keys etc. + * The (void* cred) parameter is a structure that depends on the + * specified type and on the current session (client or server). + * [ In order to minimize memory usage, and share credentials between + * several threads gnutls keeps a pointer to cred, and not the whole cred + * structure. Thus you will have to keep the structure allocated until + * you call gnutls_deinit(). ] + * + * For GNUTLS_CRD_ANON cred should be gnutls_anon_client_credentials_t in case of a client. + * In case of a server it should be gnutls_anon_server_credentials_t. + * + * For GNUTLS_CRD_SRP cred should be gnutls_srp_client_credentials_t + * in case of a client, and gnutls_srp_server_credentials_t, in case + * of a server. + * + * For GNUTLS_CRD_CERTIFICATE cred should be gnutls_certificate_credentials_t. + * + **/ +int +gnutls_credentials_set (gnutls_session_t session, + gnutls_credentials_type_t type, void *cred) +{ + auth_cred_st *ccred = NULL, *pcred = NULL; + int exists = 0; + + if (session->key->cred == NULL) + { /* beginning of the list */ + + session->key->cred = gnutls_malloc (sizeof (auth_cred_st)); + if (session->key->cred == NULL) + return GNUTLS_E_MEMORY_ERROR; + + /* copy credentials locally */ + session->key->cred->credentials = cred; + + session->key->cred->next = NULL; + session->key->cred->algorithm = type; + } + else + { + ccred = session->key->cred; + while (ccred != NULL) + { + if (ccred->algorithm == type) + { + exists = 1; + break; + } + pcred = ccred; + ccred = ccred->next; + } + /* After this, pcred is not null. + */ + + if (exists == 0) + { /* new entry */ + pcred->next = gnutls_malloc (sizeof (auth_cred_st)); + if (pcred->next == NULL) + return GNUTLS_E_MEMORY_ERROR; + + ccred = pcred->next; + + /* copy credentials locally */ + ccred->credentials = cred; + + ccred->next = NULL; + ccred->algorithm = type; + } + else + { /* modify existing entry */ + gnutls_free (ccred->credentials); + ccred->credentials = cred; + } + } + + return 0; +} + +/** + * gnutls_auth_get_type - Returns the type of credentials for the current authentication schema. + * @session: is a #gnutls_session_t structure. + * + * Returns type of credentials for the current authentication schema. + * The returned information is to be used to distinguish the function used + * to access authentication data. + * + * Eg. for CERTIFICATE ciphersuites (key exchange algorithms: KX_RSA, KX_DHE_RSA), + * the same function are to be used to access the authentication data. + **/ +gnutls_credentials_type_t +gnutls_auth_get_type (gnutls_session_t session) +{ +/* This is not the credentials we must set, but the authentication data + * we get by the peer, so it should be reversed. + */ + int server = session->security_parameters.entity == GNUTLS_SERVER ? 0 : 1; + + return + _gnutls_map_kx_get_cred (_gnutls_cipher_suite_get_kx_algo + (&session->security_parameters. + current_cipher_suite), server); +} + +/** + * gnutls_auth_server_get_type - Returns the type of credentials for the server authentication schema. + * @session: is a #gnutls_session_t structure. + * + * Returns the type of credentials that were used for server authentication. + * The returned information is to be used to distinguish the function used + * to access authentication data. + * + **/ +gnutls_credentials_type_t +gnutls_auth_server_get_type (gnutls_session_t session) +{ + return + _gnutls_map_kx_get_cred (_gnutls_cipher_suite_get_kx_algo + (&session->security_parameters. + current_cipher_suite), 1); +} + +/** + * gnutls_auth_client_get_type - Returns the type of credentials for the client authentication schema. + * @session: is a #gnutls_session_t structure. + * + * Returns the type of credentials that were used for client authentication. + * The returned information is to be used to distinguish the function used + * to access authentication data. + * + **/ +gnutls_credentials_type_t +gnutls_auth_client_get_type (gnutls_session_t session) +{ + return + _gnutls_map_kx_get_cred (_gnutls_cipher_suite_get_kx_algo + (&session->security_parameters. + current_cipher_suite), 0); +} + + +/* + * This returns a pointer to the linked list. Don't + * free that!!! + */ +const void * +_gnutls_get_kx_cred (gnutls_session_t session, + gnutls_kx_algorithm_t algo, int *err) +{ + int server = session->security_parameters.entity == GNUTLS_SERVER ? 1 : 0; + + return _gnutls_get_cred (session->key, + _gnutls_map_kx_get_cred (algo, server), err); +} + +const void * +_gnutls_get_cred (gnutls_key_st key, gnutls_credentials_type_t type, int *err) +{ + const void *retval = NULL; + int _err = -1; + auth_cred_st *ccred; + + if (key == NULL) + goto out; + + ccred = key->cred; + while (ccred != NULL) + { + if (ccred->algorithm == type) + { + break; + } + ccred = ccred->next; + } + if (ccred == NULL) + goto out; + + _err = 0; + retval = ccred->credentials; + +out: + if (err != NULL) + *err = _err; + return retval; +} + +/*- + * _gnutls_get_auth_info - Returns a pointer to authentication information. + * @session: is a #gnutls_session_t structure. + * + * This function must be called after a succesful gnutls_handshake(). + * Returns a pointer to authentication information. That information + * is data obtained by the handshake protocol, the key exchange algorithm, + * and the TLS extensions messages. + * + * In case of GNUTLS_CRD_ANON returns a type of &anon_(server/client)_auth_info_t; + * In case of GNUTLS_CRD_CERTIFICATE returns a type of &cert_auth_info_t; + * In case of GNUTLS_CRD_SRP returns a type of &srp_(server/client)_auth_info_t; + -*/ +void * +_gnutls_get_auth_info (gnutls_session_t session) +{ + return session->key->auth_info; +} + +/*- + * _gnutls_free_auth_info - Frees the auth info structure + * @session: is a #gnutls_session_t structure. + * + * This function frees the auth info structure and sets it to + * null. It must be called since some structures contain malloced + * elements. + -*/ +void +_gnutls_free_auth_info (gnutls_session_t session) +{ + dh_info_st *dh_info; + rsa_info_st *rsa_info; + + if (session == NULL || session->key == NULL) + { + gnutls_assert (); + return; + } + + switch (session->key->auth_info_type) + { + case GNUTLS_CRD_SRP: + break; + case GNUTLS_CRD_ANON: + { + anon_auth_info_t info = _gnutls_get_auth_info (session); + + if (info == NULL) + break; + + dh_info = &info->dh; + _gnutls_free_dh_info (dh_info); + } + break; + case GNUTLS_CRD_CERTIFICATE: + { + unsigned int i; + cert_auth_info_t info = _gnutls_get_auth_info (session); + + if (info == NULL) + break; + + dh_info = &info->dh; + rsa_info = &info->rsa_export; + for (i = 0; i < info->ncerts; i++) + { + _gnutls_free_datum (&info->raw_certificate_list[i]); + } + + gnutls_free (info->raw_certificate_list); + info->raw_certificate_list = NULL; + info->ncerts = 0; + + _gnutls_free_dh_info (dh_info); + _gnutls_free_rsa_info (rsa_info); + } + + + break; + default: + return; + + } + + gnutls_free (session->key->auth_info); + session->key->auth_info = NULL; + session->key->auth_info_size = 0; + session->key->auth_info_type = 0; + +} + +/* This function will set the auth info structure in the key + * structure. + * If allow change is !=0 then this will allow changing the auth + * info structure to a different type. + */ +int +_gnutls_auth_info_set (gnutls_session_t session, + gnutls_credentials_type_t type, int size, + int allow_change) +{ + if (session->key->auth_info == NULL) + { + session->key->auth_info = gnutls_calloc (1, size); + if (session->key->auth_info == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + session->key->auth_info_type = type; + session->key->auth_info_size = size; + } + else + { + if (allow_change == 0) + { + /* If the credentials for the current authentication scheme, + * are not the one we want to set, then it's an error. + * This may happen if a rehandshake is performed an the + * ciphersuite which is negotiated has different authentication + * schema. + */ + if (gnutls_auth_get_type (session) != session->key->auth_info_type) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + } + else + { + /* The new behaviour: Here we reallocate the auth info structure + * in order to be able to negotiate different authentication + * types. Ie. perform an auth_anon and then authenticate again using a + * certificate (in order to prevent revealing the certificate's contents, + * to passive eavesdropers. + */ + if (gnutls_auth_get_type (session) != session->key->auth_info_type) + { + + _gnutls_free_auth_info (session); + + session->key->auth_info = calloc (1, size); + if (session->key->auth_info == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + session->key->auth_info_type = type; + session->key->auth_info_size = size; + } + } + } + return 0; +} diff --git a/src/daemon/https/tls/gnutls_auth.h b/src/daemon/https/tls/gnutls_auth.h new file mode 100644 index 00000000..07d81352 --- /dev/null +++ b/src/daemon/https/tls/gnutls_auth.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +#ifndef GNUTLS_AUTH_H +# define GNUTLS_AUTH_H + +typedef struct mod_auth_st_int +{ + const char *name; /* null terminated */ + int (*gnutls_generate_server_certificate) (gnutls_session_t, opaque **); + int (*gnutls_generate_client_certificate) (gnutls_session_t, opaque **); + int (*gnutls_generate_server_kx) (gnutls_session_t, opaque **); + int (*gnutls_generate_client_kx) (gnutls_session_t, opaque **); /* used in SRP */ + int (*gnutls_generate_client_cert_vrfy) (gnutls_session_t, opaque **); + int (*gnutls_generate_server_certificate_request) (gnutls_session_t, + opaque **); + + int (*gnutls_process_server_certificate) (gnutls_session_t, opaque *, + size_t); + int (*gnutls_process_client_certificate) (gnutls_session_t, opaque *, + size_t); + int (*gnutls_process_server_kx) (gnutls_session_t, opaque *, size_t); + int (*gnutls_process_client_kx) (gnutls_session_t, opaque *, size_t); + int (*gnutls_process_client_cert_vrfy) (gnutls_session_t, opaque *, size_t); + int (*gnutls_process_server_certificate_request) (gnutls_session_t, + opaque *, size_t); +} mod_auth_st; + +#endif diff --git a/src/daemon/https/tls/gnutls_auth_int.h b/src/daemon/https/tls/gnutls_auth_int.h new file mode 100644 index 00000000..85fe53a1 --- /dev/null +++ b/src/daemon/https/tls/gnutls_auth_int.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +const void *_gnutls_get_cred (gnutls_key_st key, + gnutls_credentials_type_t kx, int *err); +const void *_gnutls_get_kx_cred (gnutls_session_t session, + gnutls_kx_algorithm_t algo, int *err); +void *_gnutls_get_auth_info (gnutls_session_t session); +int _gnutls_auth_info_set (gnutls_session_t session, + gnutls_credentials_type_t type, int size, + int allow_change); diff --git a/src/daemon/https/tls/gnutls_buffer.h b/src/daemon/https/tls/gnutls_buffer.h new file mode 100644 index 00000000..5c552a7a --- /dev/null +++ b/src/daemon/https/tls/gnutls_buffer.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +#include + +typedef gnutls_string gnutls_buffer; + +#define _gnutls_buffer_init(buf) _gnutls_string_init(buf, gnutls_malloc, gnutls_realloc, gnutls_free); +#define _gnutls_buffer_clear _gnutls_string_clear +#define _gnutls_buffer_append _gnutls_string_append_data diff --git a/src/daemon/https/tls/gnutls_buffers.c b/src/daemon/https/tls/gnutls_buffers.c new file mode 100644 index 00000000..5a272dca --- /dev/null +++ b/src/daemon/https/tls/gnutls_buffers.c @@ -0,0 +1,1241 @@ +/* + * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +/* This is the only file that uses the berkeley sockets API. + * + * Also holds all the buffering code used in gnutls. + * The buffering code works as: + * + * RECORD LAYER: + * 1. uses a buffer to hold data (application/handshake), + * we got but they were not requested, yet. + * (see gnutls_record_buffer_put(), gnutls_record_buffer_get_size() etc.) + * + * 2. uses a buffer to hold data that were incomplete (ie the read/write + * was interrupted) + * (see _gnutls_io_read_buffered(), _gnutls_io_write_buffered() etc.) + * + * HANDSHAKE LAYER: + * 1. Uses a buffer to hold data that was not sent or received + * complete. (E.g. sent 10 bytes of a handshake packet that is 20 bytes + * long). + * (see _gnutls_handshake_send_int(), _gnutls_handshake_recv_int()) + * + * 2. Uses buffer to hold the last received handshake message. + * (see _gnutls_handshake_buffer_put() etc.) + * + */ + +#include +#include +#include +#include +#include + +#include + +#ifdef _WIN32 +# include +#endif + +#ifndef EAGAIN +# define EAGAIN EWOULDBLOCK +#endif + +#ifdef IO_DEBUG +# include +#endif + +/** + * gnutls_transport_set_errno: + * @session: is a #gnutls_session_t structure. + * @err: error value to store in session-specific errno variable. + * + * Store @err in the session-specific errno variable. Useful values + * for @err is EAGAIN and EINTR, other values are treated will be + * treated as real errors in the push/pull function. + * + * This function is useful in replacement push/pull functions set by + * gnutls_transport_set_push_function and + * gnutls_transport_set_pullpush_function under Windows, where the + * replacement push/pull may not have access to the same @errno + * variable that is used by GnuTLS (e.g., the application is linked to + * msvcr71.dll and gnutls is linked to msvcrt.dll). + * + * If you don't have the @session variable easily accessible from the + * push/pull function, and don't worry about thread conflicts, you can + * also use gnutls_transport_set_global_errno(). + **/ +void gnutls_transport_set_errno(gnutls_session_t session, + int err) +{ + session->internals.errnum = err; +} + +/** + * gnutls_transport_set_global_errno: + * @err: error value to store in global errno variable. + * + * Store @err in the global errno variable. Useful values for @err is + * EAGAIN and EINTR, other values are treated will be treated as real + * errors in the push/pull function. + * + * This function is useful in replacement push/pull functions set by + * gnutls_transport_set_push_function and + * gnutls_transport_set_pullpush_function under Windows, where the + * replacement push/pull may not have access to the same @errno + * variable that is used by GnuTLS (e.g., the application is linked to + * msvcr71.dll and gnutls is linked to msvcrt.dll). + * + * Whether this function is thread safe or not depends on whether the + * global variable errno is thread safe, some system libraries make it + * a thread-local variable. When feasible, using the guaranteed + * thread-safe gnutls_transport_set_errno() may be better. + **/ +void gnutls_transport_set_global_errno(int err) +{ + errno = err; +} + +/* Buffers received packets of type APPLICATION DATA and + * HANDSHAKE DATA. + */ +int _gnutls_record_buffer_put(content_type_t type, + gnutls_session_t session, + opaque * data, + size_t length) +{ + gnutls_buffer *buf; + + if (length == 0) + return 0; + + switch (type) + { + case GNUTLS_APPLICATION_DATA: + buf = &session->internals.application_data_buffer; + _gnutls_buffers_log ("BUF[REC]: Inserted %d bytes of Data(%d)\n", + length, type); + break; + + case GNUTLS_HANDSHAKE: + buf = &session->internals.handshake_data_buffer; + _gnutls_buffers_log ("BUF[HSK]: Inserted %d bytes of Data(%d)\n", + length, type); + break; + + case GNUTLS_INNER_APPLICATION: + buf = &session->internals.ia_data_buffer; + _gnutls_buffers_log ("BUF[IA]: Inserted %d bytes of Data(%d)\n", length, + type); + break; + + default: + gnutls_assert () + ; + return GNUTLS_E_INVALID_REQUEST; + } + + if (_gnutls_buffer_append (buf, data, length) < 0) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + return 0; +} + +int _gnutls_record_buffer_get_size(content_type_t type, + gnutls_session_t session) +{ + switch (type) + { + case GNUTLS_APPLICATION_DATA: + return session->internals.application_data_buffer.length; + + case GNUTLS_HANDSHAKE: + return session->internals.handshake_data_buffer.length; + + case GNUTLS_INNER_APPLICATION: + return session->internals.ia_data_buffer.length; + + default: + return GNUTLS_E_INVALID_REQUEST; + } +} + +/** + * gnutls_record_check_pending - checks if there are any data to receive in gnutls buffers. + * @session: is a #gnutls_session_t structure. + * + * This function checks if there are any data to receive + * in the gnutls buffers. Returns the size of that data or 0. + * Notice that you may also use select() to check for data in + * a TCP connection, instead of this function. + * (gnutls leaves some data in the tcp buffer in order for select + * to work). + **/ +size_t gnutls_record_check_pending(gnutls_session_t session) +{ + return _gnutls_record_buffer_get_size(GNUTLS_APPLICATION_DATA, session); +} + +int _gnutls_record_buffer_get(content_type_t type, + gnutls_session_t session, + opaque * data, + size_t length) +{ + if (length == 0 || data == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + switch (type) + { + case GNUTLS_APPLICATION_DATA: + + if (length > session->internals.application_data_buffer.length) + { + length = session->internals.application_data_buffer.length; + } + + _gnutls_buffers_log ("BUFFER[REC][AD]: Read %d bytes of Data(%d)\n", + length, type); + + session->internals.application_data_buffer.length -= length; + memcpy(data, session->internals.application_data_buffer.data, length); + + /* overwrite buffer */ + memmove(session->internals.application_data_buffer.data, + &session->internals.application_data_buffer.data[length], + session->internals.application_data_buffer.length); + + /* we do no longer realloc the application_data_buffer.data, + * since it serves no practical reason. It also decreases + * performance. + */ + break; + + case GNUTLS_HANDSHAKE: + if (length > session->internals.handshake_data_buffer.length) + { + length = session->internals.handshake_data_buffer.length; + } + + _gnutls_buffers_log ("BUF[REC][HD]: Read %d bytes of Data(%d)\n", + length, type); + + session->internals.handshake_data_buffer.length -= length; + memcpy(data, session->internals.handshake_data_buffer.data, length); + + /* overwrite buffer */ + memmove(session->internals.handshake_data_buffer.data, + &session->internals.handshake_data_buffer.data[length], + session->internals.handshake_data_buffer.length); + + break; + + case GNUTLS_INNER_APPLICATION: + if (length > session->internals.ia_data_buffer.length) + length = session->internals.ia_data_buffer.length; + + _gnutls_buffers_log ("BUF[REC][IA]: Read %d bytes of Data(%d)\n", + length, type); + + session->internals.ia_data_buffer.length -= length; + memcpy(data, session->internals.ia_data_buffer.data, length); + + /* overwrite buffer */ + memmove(session->internals.ia_data_buffer.data, + &session->internals.ia_data_buffer.data[length], + session->internals.ia_data_buffer.length); + + break; + + default: + gnutls_assert () + ; + return GNUTLS_E_INVALID_REQUEST; + } + + return length; +} + +/* This function is like read. But it does not return -1 on error. + * It does return gnutls_errno instead. + * + * Flags are only used if the default recv() function is being used. + */ +static ssize_t _gnutls_read(gnutls_session_t session, + void *iptr, + size_t sizeOfPtr, + int flags) +{ + size_t left; + ssize_t i = 0; + char *ptr = iptr; + unsigned j, x, sum = 0; + gnutls_transport_ptr_t fd = session->internals.transport_recv_ptr; + + session->internals.direction = 0; + + left = sizeOfPtr; + while (left > 0) + { + + session->internals.errnum = 0; + + if (session->internals._gnutls_pull_func == NULL) + { + i = recv(GNUTLS_POINTER_TO_INT (fd), &ptr[sizeOfPtr - left], left, + flags); +#if HAVE_WINSOCK + if (i < 0) + { + int tmperr = WSAGetLastError (); + switch (tmperr) + { + case WSAEWOULDBLOCK: + session->internals.errnum = EAGAIN; + break; + + case WSAEINTR: + session->internals.errnum = EINTR; + break; + + default: + session->internals.errnum = EIO; + break; + } + WSASetLastError (tmperr); + } +#endif + } + else + i = session->internals._gnutls_pull_func(fd, &ptr[sizeOfPtr - + left], left); + + if (i < 0) + { + int err = session->internals.errnum ? session->internals.errnum + : errno; + + _gnutls_read_log ("READ: %d returned from %d, errno=%d gerrno=%d\n", + i, fd, errno, session->internals.errnum); + + if (err == EAGAIN || err == EINTR) + { + if (sizeOfPtr - left > 0) + { + + _gnutls_read_log ("READ: returning %d bytes from %d\n", + sizeOfPtr - left, fd); + + goto finish; + } + gnutls_assert (); + + if (err == EAGAIN) + return GNUTLS_E_AGAIN; + return GNUTLS_E_INTERRUPTED; + } + else + { + gnutls_assert (); + return GNUTLS_E_PULL_ERROR; + } + } + else + { + + _gnutls_read_log ("READ: Got %d bytes from %d\n", i, fd); + + if (i == 0) + break; /* EOF */ + } + + left -= i; + + } + + finish: + + if (_gnutls_log_level >= 7) + { + char line[128]; + char tmp[16]; + + _gnutls_read_log ("READ: read %d bytes from %d\n", (sizeOfPtr - left), + fd); + + for (x = 0; x < ((sizeOfPtr - left) / 16) + 1; x++) + { + line[0] = 0; + + sprintf(tmp, "%.4x - ", x); + _gnutls_str_cat(line, sizeof (line), tmp); + + for (j = 0; j < 16; j++) + { + if (sum < (sizeOfPtr - left)) + { + sprintf(tmp, "%.2x ", ((unsigned char *) ptr)[sum++]); + _gnutls_str_cat(line, sizeof (line), tmp); + } + } + _gnutls_read_log ("%s\n", line); + } + } + + return (sizeOfPtr - left); +} + +#define RCVLOWAT session->internals.lowat + +/* This function is only used with berkeley style sockets. + * Clears the peeked data (read with MSG_PEEK). + */ +int _gnutls_io_clear_peeked_data(gnutls_session_t session) +{ + char *peekdata; + int ret, sum; + + if (session->internals.have_peeked_data == 0 || RCVLOWAT == 0) + return 0; + + peekdata = gnutls_alloca (RCVLOWAT); + if (peekdata == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + /* this was already read by using MSG_PEEK - so it shouldn't fail */ + sum = 0; + do + { /* we need this to finish now */ + ret = _gnutls_read(session, peekdata, RCVLOWAT - sum, 0); + if (ret > 0) + sum += ret; + } while (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN || sum + < RCVLOWAT); + + gnutls_afree (peekdata); + + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + session->internals.have_peeked_data = 0; + + return 0; +} + +void _gnutls_io_clear_read_buffer(gnutls_session_t session) +{ + session->internals.record_recv_buffer.length = 0; +} + +/* This function is like recv(with MSG_PEEK). But it does not return -1 on error. + * It does return gnutls_errno instead. + * This function reads data from the socket and keeps them in a buffer, of up to + * MAX_RECV_SIZE. + * + * This is not a general purpose function. It returns EXACTLY the data requested, + * which are stored in a local (in the session) buffer. A pointer (iptr) to this buffer is returned. + * + */ +ssize_t _gnutls_io_read_buffered(gnutls_session_t session, + opaque ** iptr, + size_t sizeOfPtr, + content_type_t recv_type) +{ + ssize_t ret = 0, ret2 = 0; + size_t min; + int buf_pos; + opaque *buf; + int recvlowat; + int recvdata, alloc_size; + + *iptr = session->internals.record_recv_buffer.data; + + if (sizeOfPtr > MAX_RECV_SIZE || sizeOfPtr == 0) + { + gnutls_assert (); /* internal error */ + return GNUTLS_E_INVALID_REQUEST; + } + + /* If an external pull function is used, then do not leave + * any data into the kernel buffer. + */ + if (session->internals._gnutls_pull_func != NULL) + { + recvlowat = 0; + } + else + { + /* leave peeked data to the kernel space only if application data + * is received and we don't have any peeked + * data in gnutls session. + */ + if (recv_type != GNUTLS_APPLICATION_DATA + && session->internals.have_peeked_data == 0) + recvlowat = 0; + else + recvlowat = RCVLOWAT; + } + + /* calculate the actual size, ie. get the minimum of the + * buffered data and the requested data. + */ + min = MIN(session->internals.record_recv_buffer.length, sizeOfPtr); + if (min > 0) + { + /* if we have enough buffered data + * then just return them. + */ + if (min == sizeOfPtr) + { + return min; + } + } + + /* min is over zero. recvdata is the data we must + * receive in order to return the requested data. + */ + recvdata = sizeOfPtr - min; + + /* Check if the previously read data plus the new data to + * receive are longer than the maximum receive buffer size. + */ + if ((session->internals.record_recv_buffer.length + recvdata) > MAX_RECV_SIZE) + { + gnutls_assert (); /* internal error */ + return GNUTLS_E_INVALID_REQUEST; + } + + /* Allocate the data required to store the new packet. + */ + alloc_size = recvdata + session->internals.record_recv_buffer.length; + session->internals.record_recv_buffer.data + = gnutls_realloc_fast(session->internals.record_recv_buffer.data, + alloc_size); + if (session->internals.record_recv_buffer.data == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + buf_pos = session->internals.record_recv_buffer.length; + buf = session->internals.record_recv_buffer.data; + *iptr = buf; + + /* READ DATA - but leave RCVLOWAT bytes in the kernel buffer. */ + if (recvdata - recvlowat > 0) + { + ret = _gnutls_read(session, &buf[buf_pos], recvdata - recvlowat, 0); + + /* return immediately if we got an interrupt or eagain + * error. + */ + if (ret < 0 && gnutls_error_is_fatal(ret) == 0) + { + return ret; + } + } + + /* copy fresh data to our buffer. + */ + if (ret > 0) + { + _gnutls_read_log ("RB: Have %d bytes into buffer. Adding %d bytes.\n", + session->internals.record_recv_buffer.length, ret); + _gnutls_read_log ("RB: Requested %d bytes\n", sizeOfPtr); + session->internals.record_recv_buffer.length += ret; + } + + buf_pos = session->internals.record_recv_buffer.length; + + /* This is a hack placed in order for select to work. Just leave recvlowat data, + * into the kernel buffer (using a read with MSG_PEEK), thus making + * select think, that the socket is ready for reading. + * MSG_PEEK is only used with berkeley style sockets. + */ + if (ret == (recvdata - recvlowat) && recvlowat > 0) + { + ret2 = _gnutls_read(session, &buf[buf_pos], recvlowat, MSG_PEEK); + + if (ret2 < 0 && gnutls_error_is_fatal(ret2) == 0) + { + return ret2; + } + + if (ret2 > 0) + { + _gnutls_read_log ("RB-PEEK: Read %d bytes in PEEK MODE.\n", ret2); + _gnutls_read_log + ("RB-PEEK: Have %d bytes into buffer. Adding %d bytes.\nRB: Requested %d bytes\n", + session->internals.record_recv_buffer.length, ret2, sizeOfPtr); + session->internals.have_peeked_data = 1; + session->internals.record_recv_buffer.length += ret2; + + } + } + + if (ret < 0 || ret2 < 0) + { + gnutls_assert (); + /* that's because they are initialized to 0 */ + return MIN(ret, ret2); + } + + ret += ret2; + + if (ret > 0 && ret < recvlowat) + { + gnutls_assert (); + return GNUTLS_E_AGAIN; + } + + if (ret == 0) + { /* EOF */ + gnutls_assert (); + return 0; + } + + ret = session->internals.record_recv_buffer.length; + + if ((ret > 0) && ((size_t) ret < sizeOfPtr)) + { + /* Short Read */ + gnutls_assert (); + return GNUTLS_E_AGAIN; + } + else + { + return ret; + } +} + +/* These two functions are used to insert data to the send buffer of the handshake or + * record protocol. The send buffer is kept if a send is interrupted and we need to keep + * the data left to sent, in order to send them later. + */ + +#define MEMSUB(x,y) ((ssize_t)((ptrdiff_t)x-(ptrdiff_t)y)) + +inline static int _gnutls_buffer_insert(gnutls_buffer * buffer, + const opaque * _data, + size_t data_size) +{ + + if ((MEMSUB (_data, buffer->data) >= 0) && (MEMSUB (_data, buffer->data) < (ssize_t) buffer->length)) + { + /* the given _data is part of the buffer. + */ + if (data_size > buffer->length) + { + gnutls_assert (); + /* this shouldn't have happened */ + return GNUTLS_E_INTERNAL_ERROR; + } + + if (_data == buffer->data) + { /* then don't even memmove */ + buffer->length = data_size; + return 0; + } + + memmove(buffer->data, _data, data_size); + buffer->length = data_size; + + return 0; + + } + + if (_gnutls_buffer_append (buffer, _data, data_size) < 0) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + return 0; +} + +inline static int _gnutls_buffer_get(gnutls_buffer * buffer, + const opaque ** ptr, + size_t * ptr_size) +{ + *ptr_size = buffer->length; + *ptr = buffer->data; + + return 0; +} + +/* This function is like write. But it does not return -1 on error. + * It does return gnutls_errno instead. + * + * In case of E_AGAIN and E_INTERRUPTED errors, you must call gnutls_write_flush(), + * until it returns ok (0). + * + * We need to push exactly the data in n, since we cannot send less + * data. In TLS the peer must receive the whole packet in order + * to decrypt and verify the integrity. + * + */ +ssize_t _gnutls_io_write_buffered(gnutls_session_t session, + const void *iptr, + size_t n) +{ + size_t left; + unsigned j, x, sum = 0; + ssize_t retval, i; + const opaque *ptr; + int ret; + gnutls_transport_ptr_t fd = session->internals.transport_send_ptr; + + /* to know where the procedure was interrupted. + */ + session->internals.direction = 1; + + ptr = iptr; + + /* In case the previous write was interrupted, check if the + * iptr != NULL and we have data in the buffer. + * If this is true then return an error. + */ + if (session->internals.record_send_buffer.length > 0 && iptr != NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + /* If data in the buffer exist + */ + if (iptr == NULL) + { + /* checking is handled above */ + ret + = _gnutls_buffer_get(&session->internals.record_send_buffer, &ptr, &n); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + _gnutls_write_log ("WRITE: Restoring old write. (%d bytes to send)\n", + n); + } + + _gnutls_write_log ("WRITE: Will write %d bytes to %d.\n", n, fd); + + i = 0; + left = n; + while (left > 0) + { + + session->internals.errnum = 0; + + if (session->internals._gnutls_push_func == NULL) + { + i = send(GNUTLS_POINTER_TO_INT (fd), &ptr[n - left], left, 0); +#if HAVE_WINSOCK + if (i < 0) + { + int tmperr = WSAGetLastError (); + switch (tmperr) + { + case WSAEWOULDBLOCK: + session->internals.errnum = EAGAIN; + break; + + case WSAEINTR: + session->internals.errnum = EINTR; + break; + + default: + session->internals.errnum = EIO; + break; + } + WSASetLastError (tmperr); + } +#endif + } + else + i = session->internals._gnutls_push_func(fd, &ptr[n - left], left); + + if (i == -1) + { + int err = session->internals.errnum ? session->internals.errnum + : errno; + + if (err == EAGAIN || err == EINTR) + { + session->internals.record_send_buffer_prev_size += n - left; + + retval = _gnutls_buffer_insert(&session->internals. + record_send_buffer, &ptr[n - left], left); + if (retval < 0) + { + gnutls_assert (); + return retval; + } + + _gnutls_write_log + ("WRITE: Interrupted. Stored %d bytes to buffer. Already sent %d bytes.\n", + left, n - left); + + if (err == EAGAIN) + return GNUTLS_E_AGAIN; + return GNUTLS_E_INTERRUPTED; + } + else + { + gnutls_assert (); + return GNUTLS_E_PUSH_ERROR; + } + } + left -= i; + + if (_gnutls_log_level >= 7) + { + char line[128]; + char tmp[16]; + + _gnutls_write_log + ("WRITE: wrote %d bytes to %d. Left %d bytes. Total %d bytes.\n", + i, fd, left, n); + for (x = 0; x < (unsigned) ((i) / 16) + 1; x++) + { + line[0] = 0; + + if (sum > n - left) + break; + + sprintf(tmp, "%.4x - ", x); + _gnutls_str_cat(line, sizeof (line), tmp); + + for (j = 0; j < 16; j++) + { + if (sum < n - left) + { + sprintf(tmp, "%.2x ", ((unsigned char *) ptr)[sum++]); + _gnutls_str_cat(line, sizeof (line), tmp); + } + else + break; + } + _gnutls_write_log ("%s\n", line); + } + } + } + + retval = n + session->internals.record_send_buffer_prev_size; + + session->internals.record_send_buffer.length = 0; + session->internals.record_send_buffer_prev_size = 0; + + return retval; + +} + +/* This function writes the data that are left in the + * TLS write buffer (ie. because the previous write was + * interrupted. + */ +ssize_t _gnutls_io_write_flush(gnutls_session_t session) +{ + ssize_t ret; + + if (session->internals.record_send_buffer.length == 0) + return 0; /* done */ + + ret = _gnutls_io_write_buffered(session, NULL, 0); + _gnutls_write_log ("WRITE FLUSH: %d [buffer: %d]\n", ret, + session->internals.record_send_buffer.length); + + return ret; +} + +/* This function writes the data that are left in the + * Handshake write buffer (ie. because the previous write was + * interrupted. + */ +ssize_t _gnutls_handshake_io_write_flush(gnutls_session_t session) +{ + ssize_t ret; + ret = _gnutls_handshake_io_send_int(session, 0, 0, NULL, 0); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + _gnutls_write_log ("HANDSHAKE_FLUSH: written[1] %d bytes\n", ret); + + if (session->internals.handshake_send_buffer.length == 0) + { + ret = session->internals.handshake_send_buffer_prev_size; /* done */ + session->internals.handshake_send_buffer_prev_size = 0; + } + + return ret; +} + +/* This is a send function for the gnutls handshake + * protocol. Just makes sure that all data have been sent. + */ +ssize_t _gnutls_handshake_io_send_int(gnutls_session_t session, + content_type_t type, + gnutls_handshake_description_t htype, + const void *iptr, + size_t n) +{ + size_t left; + ssize_t ret = 0; + const opaque *ptr; + ssize_t retval = 0; + + ptr = iptr; + + if (session->internals.handshake_send_buffer.length > 0 && ptr == NULL && n + == 0) + { + /* resuming previously interrupted write + */ + gnutls_assert (); + ret = _gnutls_buffer_get(&session->internals.handshake_send_buffer, &ptr, + &n); + if (ret < 0) + { + gnutls_assert (); + return retval; + } + + type = session->internals.handshake_send_buffer_type; + htype = session->internals.handshake_send_buffer_htype; + + } + else if (session->internals.handshake_send_buffer.length > 0) + { + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } +#ifdef WRITE_DEBUG + else + { + size_t sum = 0, x, j; + + _gnutls_write_log ("HWRITE: will write %d bytes to %d.\n", n, + gnutls_transport_get_ptr (session)); + for (x = 0; x < ((n) / 16) + 1; x++) + { + if (sum> n) + break; + + _gnutls_write_log ("%.4x - ", x); + for (j = 0; j < 16; j++) + { + if (sum < n) + { + _gnutls_write_log ("%.2x ", ((unsigned char *) ptr)[sum++]); + } + else + break; + } + _gnutls_write_log ("\n"); + } + _gnutls_write_log ("\n"); + } +#endif + + if (n == 0) + { /* if we have no data to send */ + gnutls_assert (); + return 0; + } + else if (ptr == NULL) + { + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + left = n; + while (left > 0) + { + ret = _gnutls_send_int(session, type, htype, &ptr[n - left], left); + + if (ret <= 0) + { + if (ret == 0) + { + gnutls_assert (); + ret = GNUTLS_E_INTERNAL_ERROR; + } + + if (left > 0 + && (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN)) + { + gnutls_assert (); + + retval = _gnutls_buffer_insert(&session->internals. + handshake_send_buffer, &ptr[n - left], left); + if (retval < 0) + { + gnutls_assert (); + return retval; + } + + session->internals.handshake_send_buffer_prev_size += n - left; + + session->internals.handshake_send_buffer_type = type; + session->internals.handshake_send_buffer_htype = htype; + + } + else + { + session->internals.handshake_send_buffer_prev_size = 0; + session->internals.handshake_send_buffer.length = 0; + } + + gnutls_assert (); + return ret; + } + left -= ret; + } + + retval = n + session->internals.handshake_send_buffer_prev_size; + + session->internals.handshake_send_buffer.length = 0; + session->internals.handshake_send_buffer_prev_size = 0; + + return retval; + +} + +/* This is a receive function for the gnutls handshake + * protocol. Makes sure that we have received all data. + */ +ssize_t _gnutls_handshake_io_recv_int(gnutls_session_t session, + content_type_t type, + gnutls_handshake_description_t htype, + void *iptr, + size_t sizeOfPtr) +{ + size_t left; + ssize_t i; + opaque *ptr; + size_t dsize; + + ptr = iptr; + left = sizeOfPtr; + + if (sizeOfPtr == 0 || iptr == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + if (session->internals.handshake_recv_buffer.length > 0) + { + /* if we have already received some data */ + if (sizeOfPtr <= session->internals.handshake_recv_buffer.length) + { + /* if requested less data then return it. + */ + gnutls_assert (); + memcpy(iptr, session->internals.handshake_recv_buffer.data, sizeOfPtr); + + session->internals.handshake_recv_buffer.length -= sizeOfPtr; + + memmove(session->internals.handshake_recv_buffer.data, + &session->internals.handshake_recv_buffer. + data[sizeOfPtr], + session->internals.handshake_recv_buffer.length); + + return sizeOfPtr; + } + gnutls_assert (); + memcpy(iptr, session->internals.handshake_recv_buffer.data, + session->internals.handshake_recv_buffer.length); + + htype = session->internals.handshake_recv_buffer_htype; + type = session->internals.handshake_recv_buffer_type; + + left -= session->internals.handshake_recv_buffer.length; + + session->internals.handshake_recv_buffer.length = 0; + } + + while (left > 0) + { + dsize = sizeOfPtr - left; + i = _gnutls_recv_int(session, type, htype, &ptr[dsize], left); + if (i < 0) + { + + if (dsize > 0 && (i == GNUTLS_E_INTERRUPTED || i == GNUTLS_E_AGAIN)) + { + gnutls_assert (); + + session->internals.handshake_recv_buffer.data + = gnutls_realloc_fast(session->internals. + handshake_recv_buffer.data, dsize); + if (session->internals.handshake_recv_buffer.data == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + memcpy(session->internals.handshake_recv_buffer.data, iptr, dsize); + + session->internals.handshake_recv_buffer_htype = htype; + session->internals.handshake_recv_buffer_type = type; + + session->internals.handshake_recv_buffer.length = dsize; + } + else + session->internals.handshake_recv_buffer.length = 0; + + gnutls_assert (); + + return i; + } + else + { + if (i == 0) + break; /* EOF */ + } + + left -= i; + + } + + session->internals.handshake_recv_buffer.length = 0; + + return sizeOfPtr - left; +} + +/* Buffer for handshake packets. Keeps the packets in order + * for finished messages to use them. Used in HMAC calculation + * and finished messages. + */ +int _gnutls_handshake_buffer_put(gnutls_session_t session, + opaque * data, + size_t length) +{ + + if (length == 0) + return 0; + + if ((session->internals.max_handshake_data_buffer_size > 0) && ((length + + session-> + internals. + handshake_hash_buffer. + length) > session-> + internals. + max_handshake_data_buffer_size)) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + _gnutls_buffers_log ("BUF[HSK]: Inserted %d bytes of Data\n", length); + + if (_gnutls_buffer_append (&session->internals.handshake_hash_buffer, data, + length) < 0) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + return 0; +} + +int _gnutls_handshake_buffer_get_size(gnutls_session_t session) +{ + + return session->internals.handshake_hash_buffer.length; +} + +/* this function does not touch the buffer + * and returns data from it (peek mode!) + */ +int _gnutls_handshake_buffer_peek(gnutls_session_t session, + opaque * data, + size_t length) +{ + if (length > session->internals.handshake_hash_buffer.length) + { + length = session->internals.handshake_hash_buffer.length; + } + + _gnutls_buffers_log ("BUF[HSK]: Peeked %d bytes of Data\n", length); + + memcpy(data, session->internals.handshake_hash_buffer.data, length); + return length; +} + +/* this function does not touch the buffer + * and returns data from it (peek mode!) + */ +int _gnutls_handshake_buffer_get_ptr(gnutls_session_t session, + opaque ** data_ptr, + size_t * length) +{ + if (length != NULL) + *length = session->internals.handshake_hash_buffer.length; + + _gnutls_buffers_log ("BUF[HSK]: Peeked %d bytes of Data\n", *length); + + if (data_ptr != NULL) + *data_ptr = session->internals.handshake_hash_buffer.data; + + return 0; +} + +/* Does not free the buffer + */ +int _gnutls_handshake_buffer_empty(gnutls_session_t session) +{ + + _gnutls_buffers_log ("BUF[HSK]: Emptied buffer\n"); + + session->internals.handshake_hash_buffer.length = 0; + + return 0; +} + +int _gnutls_handshake_buffer_clear(gnutls_session_t session) +{ + + _gnutls_buffers_log ("BUF[HSK]: Cleared Data from buffer\n"); + + _gnutls_buffer_clear (&session->internals.handshake_hash_buffer); + + return 0; +} diff --git a/src/daemon/https/tls/gnutls_buffers.h b/src/daemon/https/tls/gnutls_buffers.h new file mode 100644 index 00000000..aaafde0d --- /dev/null +++ b/src/daemon/https/tls/gnutls_buffers.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +int _gnutls_record_buffer_put (content_type_t type, + gnutls_session_t session, opaque * data, + size_t length); +int _gnutls_record_buffer_get_size (content_type_t type, + gnutls_session_t session); +int _gnutls_record_buffer_get (content_type_t type, + gnutls_session_t session, opaque * data, + size_t length); +ssize_t _gnutls_io_read_buffered (gnutls_session_t, opaque ** iptr, + size_t n, content_type_t); +void _gnutls_io_clear_read_buffer (gnutls_session_t); +int _gnutls_io_clear_peeked_data (gnutls_session_t session); + +ssize_t _gnutls_io_write_buffered (gnutls_session_t, const void *iptr, + size_t n); +ssize_t _gnutls_io_write_buffered2 (gnutls_session_t, const void *iptr, + size_t n, const void *iptr2, size_t n2); + +int _gnutls_handshake_buffer_get_size (gnutls_session_t session); +int _gnutls_handshake_buffer_peek (gnutls_session_t session, opaque * data, + size_t length); +int _gnutls_handshake_buffer_put (gnutls_session_t session, opaque * data, + size_t length); +int _gnutls_handshake_buffer_clear (gnutls_session_t session); +int _gnutls_handshake_buffer_empty (gnutls_session_t session); +int _gnutls_handshake_buffer_get_ptr (gnutls_session_t session, + opaque ** data_ptr, size_t * length); + +#define _gnutls_handshake_io_buffer_clear( session) \ + _gnutls_buffer_clear( &session->internals.handshake_send_buffer); \ + _gnutls_buffer_clear( &session->internals.handshake_recv_buffer); \ + session->internals.handshake_send_buffer_prev_size = 0 + +ssize_t _gnutls_handshake_io_recv_int (gnutls_session_t, content_type_t, + gnutls_handshake_description_t, void *, + size_t); +ssize_t _gnutls_handshake_io_send_int (gnutls_session_t, content_type_t, + gnutls_handshake_description_t, + const void *, size_t); +ssize_t _gnutls_io_write_flush (gnutls_session_t session); +ssize_t _gnutls_handshake_io_write_flush (gnutls_session_t session); + +size_t gnutls_record_check_pending (gnutls_session_t session); diff --git a/src/daemon/https/tls/gnutls_cert.c b/src/daemon/https/tls/gnutls_cert.c new file mode 100644 index 00000000..0160d644 --- /dev/null +++ b/src/daemon/https/tls/gnutls_cert.c @@ -0,0 +1,918 @@ +/* + * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +/* Some of the stuff needed for Certificate authentication is contained + * in this file. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* x509 */ +#include "x509.h" +#include "mpi.h" + +/** + * gnutls_certificate_free_keys - Used to free all the keys from a gnutls_certificate_credentials_t structure + * @sc: is an #gnutls_certificate_credentials_t structure. + * + * This function will delete all the keys and the certificates associated + * with the given credentials. This function must not be called when a + * TLS negotiation that uses the credentials is in progress. + * + **/ +void +gnutls_certificate_free_keys (gnutls_certificate_credentials_t sc) +{ + unsigned i, j; + + for (i = 0; i < sc->ncerts; i++) + { + for (j = 0; j < sc->cert_list_length[i]; j++) + { + _gnutls_gcert_deinit (&sc->cert_list[i][j]); + } + gnutls_free (sc->cert_list[i]); + } + + gnutls_free (sc->cert_list_length); + sc->cert_list_length = NULL; + + gnutls_free (sc->cert_list); + sc->cert_list = NULL; + + for (i = 0; i < sc->ncerts; i++) + { + _gnutls_gkey_deinit (&sc->pkey[i]); + } + + gnutls_free (sc->pkey); + sc->pkey = NULL; + + sc->ncerts = 0; + +} + +/** + * gnutls_certificate_free_cas - Used to free all the CAs from a gnutls_certificate_credentials_t structure + * @sc: is an #gnutls_certificate_credentials_t structure. + * + * This function will delete all the CAs associated + * with the given credentials. Servers that do not use + * gnutls_certificate_verify_peers2() may call this to + * save some memory. + * + **/ +void +gnutls_certificate_free_cas (gnutls_certificate_credentials_t sc) +{ + unsigned j; + + for (j = 0; j < sc->x509_ncas; j++) + { + gnutls_x509_crt_deinit (sc->x509_ca_list[j]); + } + + sc->x509_ncas = 0; + + gnutls_free (sc->x509_ca_list); + sc->x509_ca_list = NULL; + +} + +/** + * gnutls_certificate_free_ca_names - Used to free all the CA names from a gnutls_certificate_credentials_t structure + * @sc: is an #gnutls_certificate_credentials_t structure. + * + * This function will delete all the CA name in the + * given credentials. Clients may call this to save some memory + * since in client side the CA names are not used. + * + * CA names are used by servers to advertize the CAs they + * support to clients. + * + **/ +void +gnutls_certificate_free_ca_names (gnutls_certificate_credentials_t sc) +{ + _gnutls_free_datum (&sc->x509_rdn_sequence); +} + +/*- + * _gnutls_certificate_get_rsa_params - Returns the RSA parameters pointer + * @rsa_params: holds the RSA parameters or NULL. + * @func: function to retrieve the parameters or NULL. + * @session: The session. + * + * This function will return the rsa parameters pointer. + * + -*/ +gnutls_rsa_params_t +_gnutls_certificate_get_rsa_params (gnutls_rsa_params_t rsa_params, + gnutls_params_function * func, + gnutls_session_t session) +{ + gnutls_params_st params; + int ret; + + if (session->internals.params.rsa_params) + { + return session->internals.params.rsa_params; + } + + if (rsa_params) + { + session->internals.params.rsa_params = rsa_params; + } + else if (func) + { + ret = func (session, GNUTLS_PARAMS_RSA_EXPORT, ¶ms); + if (ret == 0 && params.type == GNUTLS_PARAMS_RSA_EXPORT) + { + session->internals.params.rsa_params = params.params.rsa_export; + session->internals.params.free_rsa_params = params.deinit; + } + } + + return session->internals.params.rsa_params; +} + + +/** + * gnutls_certificate_free_credentials - Used to free an allocated gnutls_certificate_credentials_t structure + * @sc: is an #gnutls_certificate_credentials_t structure. + * + * This structure is complex enough to manipulate directly thus + * this helper function is provided in order to free (deallocate) it. + * + * This function does not free any temporary parameters associated + * with this structure (ie RSA and DH parameters are not freed by + * this function). + **/ +void +gnutls_certificate_free_credentials (gnutls_certificate_credentials_t sc) +{ + gnutls_certificate_free_keys (sc); + gnutls_certificate_free_cas (sc); + gnutls_certificate_free_ca_names (sc); +#ifdef ENABLE_PKI + gnutls_certificate_free_crls (sc); +#endif + +#ifndef KEYRING_HACK + if (_E_gnutls_openpgp_keyring_deinit) + _E_gnutls_openpgp_keyring_deinit (sc->keyring); +#else + _gnutls_free_datum (&sc->keyring); +#endif + + gnutls_free (sc); +} + + +/** + * gnutls_certificate_allocate_credentials - Used to allocate a gnutls_certificate_credentials_t structure + * @res: is a pointer to an #gnutls_certificate_credentials_t structure. + * + * This structure is complex enough to manipulate directly thus this + * helper function is provided in order to allocate it. + * + * Returns: %GNUTLS_E_SUCCESS on success, or an error code. + **/ +int +gnutls_certificate_allocate_credentials (gnutls_certificate_credentials_t * + res) +{ + *res = gnutls_calloc (1, sizeof (certificate_credentials_st)); + + if (*res == NULL) + return GNUTLS_E_MEMORY_ERROR; + + (*res)->verify_bits = DEFAULT_VERIFY_BITS; + (*res)->verify_depth = DEFAULT_VERIFY_DEPTH; + + return 0; +} + + +/* returns the KX algorithms that are supported by a + * certificate. (Eg a certificate with RSA params, supports + * GNUTLS_KX_RSA algorithm). + * This function also uses the KeyUsage field of the certificate + * extensions in order to disable unneded algorithms. + */ +int +_gnutls_selected_cert_supported_kx (gnutls_session_t session, + gnutls_kx_algorithm_t ** alg, + int *alg_size) +{ + gnutls_kx_algorithm_t kx; + gnutls_pk_algorithm_t pk; + gnutls_kx_algorithm_t kxlist[MAX_ALGOS]; + gnutls_cert *cert; + int i; + + if (session->internals.selected_cert_list_length == 0) + { + *alg_size = 0; + *alg = NULL; + return 0; + } + + cert = &session->internals.selected_cert_list[0]; + i = 0; + + for (kx = 0; kx < MAX_ALGOS; kx++) + { + pk = _gnutls_map_pk_get_pk (kx); + if (pk == cert->subject_pk_algorithm) + { + /* then check key usage */ + if (_gnutls_check_key_usage (cert, kx) == 0) + { + kxlist[i] = kx; + i++; + } + } + } + + if (i == 0) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + *alg = gnutls_calloc (1, sizeof (gnutls_kx_algorithm_t) * i); + if (*alg == NULL) + return GNUTLS_E_MEMORY_ERROR; + + *alg_size = i; + + memcpy (*alg, kxlist, i * sizeof (gnutls_kx_algorithm_t)); + + return 0; +} + + +/** + * gnutls_certificate_server_set_request - Used to set whether to request a client certificate + * @session: is an #gnutls_session_t structure. + * @req: is one of GNUTLS_CERT_REQUEST, GNUTLS_CERT_REQUIRE + * + * This function specifies if we (in case of a server) are going + * to send a certificate request message to the client. If @req + * is GNUTLS_CERT_REQUIRE then the server will return an error if + * the peer does not provide a certificate. If you do not + * call this function then the client will not be asked to + * send a certificate. + **/ +void +gnutls_certificate_server_set_request (gnutls_session_t session, + gnutls_certificate_request_t req) +{ + session->internals.send_cert_req = req; +} + +/** + * gnutls_certificate_client_set_retrieve_function - Used to set a callback to retrieve the certificate + * @cred: is a #gnutls_certificate_credentials_t structure. + * @func: is the callback function + * + * This function sets a callback to be called in order to retrieve the certificate + * to be used in the handshake. + * The callback's function prototype is: + * int (*callback)(gnutls_session_t, const gnutls_datum_t* req_ca_dn, int nreqs, + * const gnutls_pk_algorithm_t* pk_algos, int pk_algos_length, gnutls_retr_st* st); + * + * @req_ca_cert is only used in X.509 certificates. + * Contains a list with the CA names that the server considers trusted. + * Normally we should send a certificate that is signed + * by one of these CAs. These names are DER encoded. To get a more + * meaningful value use the function gnutls_x509_rdn_get(). + * + * @pk_algos contains a list with server's acceptable signature algorithms. + * The certificate returned should support the server's given algorithms. + * + * @st should contain the certificates and private keys. + * + * If the callback function is provided then gnutls will call it, in the + * handshake, after the certificate request message has been received. + * + * The callback function should set the certificate list to be sent, and + * return 0 on success. If no certificate was selected then the number of certificates + * should be set to zero. The value (-1) indicates error and the handshake + * will be terminated. + **/ +void gnutls_certificate_client_set_retrieve_function + (gnutls_certificate_credentials_t cred, + gnutls_certificate_client_retrieve_function * func) +{ + cred->client_get_cert_callback = func; +} + +/** + * gnutls_certificate_server_set_retrieve_function - Used to set a callback to retrieve the certificate + * @cred: is a #gnutls_certificate_credentials_t structure. + * @func: is the callback function + * + * This function sets a callback to be called in order to retrieve the certificate + * to be used in the handshake. + * The callback's function prototype is: + * int (*callback)(gnutls_session_t, gnutls_retr_st* st); + * + * @st should contain the certificates and private keys. + * + * If the callback function is provided then gnutls will call it, in the + * handshake, after the certificate request message has been received. + * + * The callback function should set the certificate list to be sent, and + * return 0 on success. The value (-1) indicates error and the handshake + * will be terminated. + **/ +void gnutls_certificate_server_set_retrieve_function + (gnutls_certificate_credentials_t cred, + gnutls_certificate_server_retrieve_function * func) +{ + cred->server_get_cert_callback = func; +} + +/*- + * _gnutls_x509_extract_certificate_activation_time - This function returns the peer's certificate activation time + * @cert: should contain an X.509 DER encoded certificate + * + * This function will return the certificate's activation time in UNIX time + * (ie seconds since 00:00:00 UTC January 1, 1970). + * + * Returns a (time_t) -1 in case of an error. + * + -*/ +static time_t +_gnutls_x509_get_raw_crt_activation_time (const gnutls_datum_t * cert) +{ + gnutls_x509_crt_t xcert; + time_t result; + + result = gnutls_x509_crt_init (&xcert); + if (result < 0) + return (time_t) - 1; + + result = gnutls_x509_crt_import (xcert, cert, GNUTLS_X509_FMT_DER); + if (result < 0) + { + gnutls_x509_crt_deinit (xcert); + return (time_t) - 1; + } + + result = gnutls_x509_crt_get_activation_time (xcert); + + gnutls_x509_crt_deinit (xcert); + + return result; +} + +/*- + * gnutls_x509_extract_certificate_expiration_time - This function returns the certificate's expiration time + * @cert: should contain an X.509 DER encoded certificate + * + * This function will return the certificate's expiration time in UNIX + * time (ie seconds since 00:00:00 UTC January 1, 1970). Returns a + * + * (time_t) -1 in case of an error. + * + -*/ +static time_t +_gnutls_x509_get_raw_crt_expiration_time (const gnutls_datum_t * cert) +{ + gnutls_x509_crt_t xcert; + time_t result; + + result = gnutls_x509_crt_init (&xcert); + if (result < 0) + return (time_t) - 1; + + result = gnutls_x509_crt_import (xcert, cert, GNUTLS_X509_FMT_DER); + if (result < 0) + { + gnutls_x509_crt_deinit (xcert); + return (time_t) - 1; + } + + result = gnutls_x509_crt_get_expiration_time (xcert); + + gnutls_x509_crt_deinit (xcert); + + return result; +} + +/*- + * _gnutls_openpgp_crt_verify_peers - This function returns the peer's certificate status + * @session: is a gnutls session + * + * This function will try to verify the peer's certificate and return its status (TRUSTED, INVALID etc.). + * Returns a negative error code in case of an error, or GNUTLS_E_NO_CERTIFICATE_FOUND if no certificate was sent. + * + -*/ +int +_gnutls_openpgp_crt_verify_peers (gnutls_session_t session, + unsigned int *status) +{ + cert_auth_info_t info; + gnutls_certificate_credentials_t cred; + int peer_certificate_list_size, ret; + + CHECK_AUTH (GNUTLS_CRD_CERTIFICATE, GNUTLS_E_INVALID_REQUEST); + + info = _gnutls_get_auth_info (session); + if (info == NULL) + return GNUTLS_E_INVALID_REQUEST; + + cred = (gnutls_certificate_credentials_t) + _gnutls_get_cred (session->key, GNUTLS_CRD_CERTIFICATE, NULL); + if (cred == NULL) + { + gnutls_assert (); + return GNUTLS_E_INSUFFICIENT_CREDENTIALS; + } + + if (info->raw_certificate_list == NULL || info->ncerts == 0) + { + gnutls_assert (); + return GNUTLS_E_NO_CERTIFICATE_FOUND; + } + + /* generate a list of gnutls_certs based on the auth info + * raw certs. + */ + peer_certificate_list_size = info->ncerts; + + if (peer_certificate_list_size != 1) + { + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + /* Verify certificate + */ + if (_E_gnutls_openpgp_verify_key == NULL) + { + gnutls_assert (); + return GNUTLS_E_INIT_LIBEXTRA; + } + ret = + _E_gnutls_openpgp_verify_key (cred, &info->raw_certificate_list[0], + peer_certificate_list_size, status); + + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + return 0; +} + + +/** + * gnutls_certificate_verify_peers2 - This function returns the peer's certificate verification status + * @session: is a gnutls session + * @status: is the output of the verification + * + * This function will try to verify the peer's certificate and return + * its status (trusted, invalid etc.). The value of @status should + * be one or more of the gnutls_certificate_status_t enumerated + * elements bitwise or'd. To avoid denial of service attacks some + * default upper limits regarding the certificate key size and chain + * size are set. To override them use + * gnutls_certificate_set_verify_limits(). + * + * Note that you must also check the peer's name in order to check if + * the verified certificate belongs to the actual peer. + * + * This is the same as gnutls_x509_crt_list_verify() and uses the + * loaded CAs in the credentials as trusted CAs. + * + * Note that some commonly used X.509 Certificate Authorities are + * still using Version 1 certificates. If you want to accept them, + * you need to call gnutls_certificate_set_verify_flags() with, e.g., + * %GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT parameter. + * + * Returns: a negative error code on error and zero on success. + **/ +int +gnutls_certificate_verify_peers2 (gnutls_session_t session, + unsigned int *status) +{ + cert_auth_info_t info; + + CHECK_AUTH (GNUTLS_CRD_CERTIFICATE, GNUTLS_E_INVALID_REQUEST); + + info = _gnutls_get_auth_info (session); + if (info == NULL) + { + return GNUTLS_E_NO_CERTIFICATE_FOUND; + } + + if (info->raw_certificate_list == NULL || info->ncerts == 0) + return GNUTLS_E_NO_CERTIFICATE_FOUND; + + switch (gnutls_certificate_type_get (session)) + { + case GNUTLS_CRT_X509: + return _gnutls_x509_cert_verify_peers (session, status); + case GNUTLS_CRT_OPENPGP: + return _gnutls_openpgp_crt_verify_peers (session, status); + default: + return GNUTLS_E_INVALID_REQUEST; + } +} + +/** + * gnutls_certificate_verify_peers - This function returns the peer's certificate verification status + * @session: is a gnutls session + * + * This function will try to verify the peer's certificate and return + * its status (trusted, invalid etc.). However you must also check + * the peer's name in order to check if the verified certificate + * belongs to the actual peer. + * + * The return value should be one or more of the + * gnutls_certificate_status_t enumerated elements bitwise or'd, or a + * negative value on error. + * + * This is the same as gnutls_x509_crt_list_verify(). + * + * Deprecated: Use gnutls_certificate_verify_peers2() instead. + **/ +int +gnutls_certificate_verify_peers (gnutls_session_t session) +{ + unsigned int status; + int ret; + + ret = gnutls_certificate_verify_peers2 (session, &status); + + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + return status; +} + +/** + * gnutls_certificate_expiration_time_peers - This function returns the peer's certificate expiration time + * @session: is a gnutls session + * + * This function will return the peer's certificate expiration time. + * + * Returns: (time_t)-1 on error. + **/ +time_t +gnutls_certificate_expiration_time_peers (gnutls_session_t session) +{ + cert_auth_info_t info; + + CHECK_AUTH (GNUTLS_CRD_CERTIFICATE, GNUTLS_E_INVALID_REQUEST); + + info = _gnutls_get_auth_info (session); + if (info == NULL) + { + return (time_t) - 1; + } + + if (info->raw_certificate_list == NULL || info->ncerts == 0) + { + gnutls_assert (); + return (time_t) - 1; + } + + switch (gnutls_certificate_type_get (session)) + { + case GNUTLS_CRT_X509: + return _gnutls_x509_get_raw_crt_expiration_time (&info-> + raw_certificate_list + [0]); + case GNUTLS_CRT_OPENPGP: + if (_E_gnutls_openpgp_get_raw_key_expiration_time == NULL) + return (time_t) - 1; + return _E_gnutls_openpgp_get_raw_key_expiration_time (&info-> + raw_certificate_list + [0]); + default: + return (time_t) - 1; + } +} + +/** + * gnutls_certificate_activation_time_peers - This function returns the peer's certificate activation time + * @session: is a gnutls session + * + * This function will return the peer's certificate activation time. + * This is the creation time for openpgp keys. + * + * Returns: (time_t)-1 on error. + **/ +time_t +gnutls_certificate_activation_time_peers (gnutls_session_t session) +{ + cert_auth_info_t info; + + CHECK_AUTH (GNUTLS_CRD_CERTIFICATE, GNUTLS_E_INVALID_REQUEST); + + info = _gnutls_get_auth_info (session); + if (info == NULL) + { + return (time_t) - 1; + } + + if (info->raw_certificate_list == NULL || info->ncerts == 0) + { + gnutls_assert (); + return (time_t) - 1; + } + + switch (gnutls_certificate_type_get (session)) + { + case GNUTLS_CRT_X509: + return _gnutls_x509_get_raw_crt_activation_time (&info-> + raw_certificate_list + [0]); + case GNUTLS_CRT_OPENPGP: + if (_E_gnutls_openpgp_get_raw_key_creation_time == NULL) + return (time_t) - 1; + return _E_gnutls_openpgp_get_raw_key_creation_time (&info-> + raw_certificate_list + [0]); + default: + return (time_t) - 1; + } +} + +int +_gnutls_raw_cert_to_gcert (gnutls_cert * gcert, + gnutls_certificate_type_t type, + const gnutls_datum_t * raw_cert, + int flags /* OR of ConvFlags */ ) +{ + switch (type) + { + case GNUTLS_CRT_X509: + return _gnutls_x509_raw_cert_to_gcert (gcert, raw_cert, flags); + case GNUTLS_CRT_OPENPGP: + if (_E_gnutls_openpgp_raw_key_to_gcert == NULL) + { + gnutls_assert (); + return GNUTLS_E_INIT_LIBEXTRA; + } + return _E_gnutls_openpgp_raw_key_to_gcert (gcert, raw_cert); + default: + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } +} + +int +_gnutls_raw_privkey_to_gkey (gnutls_privkey * key, + gnutls_certificate_type_t type, + const gnutls_datum_t * raw_key, + int key_enc /* DER or PEM */ ) +{ + switch (type) + { + case GNUTLS_CRT_X509: + return _gnutls_x509_raw_privkey_to_gkey (key, raw_key, key_enc); + case GNUTLS_CRT_OPENPGP: + if (_E_gnutls_openpgp_raw_privkey_to_gkey == NULL) + { + gnutls_assert (); + return GNUTLS_E_INIT_LIBEXTRA; + } + return _E_gnutls_openpgp_raw_privkey_to_gkey (key, raw_key, + (gnutls_openpgp_crt_fmt_t) + key_enc); + default: + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } +} + + +/* This function will convert a der certificate to a format + * (structure) that gnutls can understand and use. Actually the + * important thing on this function is that it extracts the + * certificate's (public key) parameters. + * + * The noext flag is used to complete the handshake even if the + * extensions found in the certificate are unsupported and critical. + * The critical extensions will be catched by the verification functions. + */ +int +_gnutls_x509_raw_cert_to_gcert (gnutls_cert * gcert, + const gnutls_datum_t * derCert, + int flags /* OR of ConvFlags */ ) +{ + int ret; + gnutls_x509_crt_t cert; + + ret = gnutls_x509_crt_init (&cert); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + ret = gnutls_x509_crt_import (cert, derCert, GNUTLS_X509_FMT_DER); + if (ret < 0) + { + gnutls_assert (); + gnutls_x509_crt_deinit (cert); + return ret; + } + + ret = _gnutls_x509_crt_to_gcert (gcert, cert, flags); + gnutls_x509_crt_deinit (cert); + + return ret; +} + +/* Like above but it accepts a parsed certificate instead. + */ +int +_gnutls_x509_crt_to_gcert (gnutls_cert * gcert, + gnutls_x509_crt_t cert, unsigned int flags) +{ + int ret = 0; + + memset (gcert, 0, sizeof (gnutls_cert)); + gcert->cert_type = GNUTLS_CRT_X509; + + if (!(flags & CERT_NO_COPY)) + { +#define SMALL_DER 512 + opaque *der; + size_t der_size = SMALL_DER; + + /* initially allocate a bogus size, just in case the certificate + * fits in it. That way we minimize the DER encodings performed. + */ + der = gnutls_malloc (SMALL_DER); + if (der == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + ret = + gnutls_x509_crt_export (cert, GNUTLS_X509_FMT_DER, der, &der_size); + if (ret < 0 && ret != GNUTLS_E_SHORT_MEMORY_BUFFER) + { + gnutls_assert (); + gnutls_free (der); + return ret; + } + + if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) + { + der = gnutls_realloc (der, der_size); + if (der == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + ret = + gnutls_x509_crt_export (cert, GNUTLS_X509_FMT_DER, der, + &der_size); + if (ret < 0) + { + gnutls_assert (); + gnutls_free (der); + return ret; + } + } + + gcert->raw.data = der; + gcert->raw.size = der_size; + } + else + /* now we have 0 or a bitwise or of things to decode */ + flags ^= CERT_NO_COPY; + + + if (flags & CERT_ONLY_EXTENSIONS || flags == 0) + { + gnutls_x509_crt_get_key_usage (cert, &gcert->key_usage, NULL); + gcert->version = gnutls_x509_crt_get_version (cert); + } + gcert->subject_pk_algorithm = gnutls_x509_crt_get_pk_algorithm (cert, NULL); + + if (flags & CERT_ONLY_PUBKEY || flags == 0) + { + gcert->params_size = MAX_PUBLIC_PARAMS_SIZE; + ret = + _gnutls_x509_crt_get_mpis (cert, gcert->params, &gcert->params_size); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + } + + return 0; + +} + +void +_gnutls_gcert_deinit (gnutls_cert * cert) +{ + int i; + + if (cert == NULL) + return; + + for (i = 0; i < cert->params_size; i++) + { + _gnutls_mpi_release (&cert->params[i]); + } + + _gnutls_free_datum (&cert->raw); +} + +/** + * gnutls_sign_callback_set: + * @session: is a gnutls session + * @sign_func: function pointer to application's sign callback. + * @userdata: void pointer that will be passed to sign callback. + * + * Set the callback function. The function must have this prototype: + * + * typedef int (*gnutls_sign_func) (gnutls_session_t session, + * void *userdata, + * gnutls_certificate_type_t cert_type, + * const gnutls_datum_t * cert, + * const gnutls_datum_t * hash, + * gnutls_datum_t * signature); + * + * The @userdata parameter is passed to the @sign_func verbatim, and + * can be used to store application-specific data needed in the + * callback function. See also gnutls_sign_callback_get(). + **/ +void +gnutls_sign_callback_set (gnutls_session_t session, + gnutls_sign_func sign_func, void *userdata) +{ + session->internals.sign_func = sign_func; + session->internals.sign_func_userdata = userdata; +} + +/** + * gnutls_sign_callback_get: + * @session: is a gnutls session + * @userdata: if non-%NULL, will be set to abstract callback pointer. + * + * Retrieve the callback function, and its userdata pointer. + * + * Returns: The function pointer set by gnutls_sign_callback_set(), or + * if not set, %NULL. + **/ +gnutls_sign_func +gnutls_sign_callback_get (gnutls_session_t session, void **userdata) +{ + if (userdata) + *userdata = session->internals.sign_func_userdata; + return session->internals.sign_func; +} diff --git a/src/daemon/https/tls/gnutls_cert.h b/src/daemon/https/tls/gnutls_cert.h new file mode 100644 index 00000000..09df10eb --- /dev/null +++ b/src/daemon/https/tls/gnutls_cert.h @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +#ifndef GNUTLS_CERT_H +# define GNUTLS_CERT_H + +#include +#include +#include "x509.h" + +#define MAX_PUBLIC_PARAMS_SIZE 4 /* ok for RSA and DSA */ + +/* parameters should not be larger than this limit */ +#define DSA_PUBLIC_PARAMS 4 +#define RSA_PUBLIC_PARAMS 2 + +/* For key Usage, test as: + * if (st.key_usage & KEY_DIGITAL_SIGNATURE) ... + */ +#define KEY_DIGITAL_SIGNATURE 128 +#define KEY_NON_REPUDIATION 64 +#define KEY_KEY_ENCIPHERMENT 32 +#define KEY_DATA_ENCIPHERMENT 16 +#define KEY_KEY_AGREEMENT 8 +#define KEY_KEY_CERT_SIGN 4 +#define KEY_CRL_SIGN 2 +#define KEY_ENCIPHER_ONLY 1 +#define KEY_DECIPHER_ONLY 32768 + +typedef struct gnutls_cert +{ + mpi_t params[MAX_PUBLIC_PARAMS_SIZE]; /* the size of params depends on the public + * key algorithm + * RSA: [0] is modulus + * [1] is public exponent + * DSA: [0] is p + * [1] is q + * [2] is g + * [3] is public key + */ + int params_size; /* holds the size of MPI params */ + + gnutls_pk_algorithm_t subject_pk_algorithm; + + unsigned int key_usage; /* bits from KEY_* + */ + + unsigned int version; + /* holds the type (PGP, X509) + */ + gnutls_certificate_type_t cert_type; + + gnutls_datum_t raw; + +} gnutls_cert; + +typedef struct gnutls_privkey_int +{ + mpi_t params[MAX_PRIV_PARAMS_SIZE]; /* the size of params depends on the public + * key algorithm + */ + /* + * RSA: [0] is modulus + * [1] is public exponent + * [2] is private exponent + * [3] is prime1 (p) + * [4] is prime2 (q) + * [5] is coefficient (u == inverse of p mod q) + * DSA: [0] is p + * [1] is q + * [2] is g + * [3] is y (public key) + * [4] is x (private key) + */ + int params_size; /* holds the number of params */ + + gnutls_pk_algorithm_t pk_algorithm; +} gnutls_privkey; + +struct gnutls_session_int; /* because gnutls_session_t is not defined when this file is included */ + +typedef enum ConvFlags +{ + CERT_NO_COPY = 2, + CERT_ONLY_PUBKEY = 4, + CERT_ONLY_EXTENSIONS = 16 +} ConvFlags; + +int _gnutls_x509_raw_cert_to_gcert (gnutls_cert * gcert, + const gnutls_datum_t * derCert, + int flags); +int _gnutls_x509_crt_to_gcert (gnutls_cert * gcert, gnutls_x509_crt_t cert, + unsigned int flags); + +void _gnutls_gkey_deinit (gnutls_privkey * key); +void _gnutls_gcert_deinit (gnutls_cert * cert); + +int _gnutls_selected_cert_supported_kx (struct gnutls_session_int *session, + gnutls_kx_algorithm_t ** alg, + int *alg_size); + +int _gnutls_raw_cert_to_gcert (gnutls_cert * gcert, + gnutls_certificate_type_t type, + const gnutls_datum_t * raw_cert, + int flags /* OR of ConvFlags */ ); +int _gnutls_raw_privkey_to_gkey (gnutls_privkey * key, + gnutls_certificate_type_t type, + const gnutls_datum_t * raw_key, + int key_enc /* DER or PEM */ ); + +#endif diff --git a/src/daemon/https/tls/gnutls_cipher.c b/src/daemon/https/tls/gnutls_cipher.c new file mode 100644 index 00000000..8245b1c0 --- /dev/null +++ b/src/daemon/https/tls/gnutls_cipher.c @@ -0,0 +1,584 @@ +/* + * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +/* Some high level functions to be used in the record encryption are + * included here. + */ + +#include "gnutls_int.h" +#include "gnutls_errors.h" +#include "gnutls_compress.h" +#include "gnutls_cipher.h" +#include "gnutls_algorithms.h" +#include "gnutls_hash_int.h" +#include "gnutls_cipher_int.h" +#include "debug.h" +#include "gnutls_num.h" +#include "gnutls_datum.h" +#include "gnutls_kx.h" +#include "gnutls_record.h" +#include "gnutls_constate.h" +#include + +inline static int +is_write_comp_null (gnutls_session_t session) +{ + if (session->security_parameters.write_compression_algorithm == + GNUTLS_COMP_NULL) + return 0; + + return 1; +} + +inline static int +is_read_comp_null (gnutls_session_t session) +{ + if (session->security_parameters.read_compression_algorithm == + GNUTLS_COMP_NULL) + return 0; + + return 1; +} + + +/* returns ciphertext which contains the headers too. This also + * calculates the size in the header field. + * + * If random pad != 0 then the random pad data will be appended. + */ +int +_gnutls_encrypt (gnutls_session_t session, const opaque * headers, + size_t headers_size, const opaque * data, + size_t data_size, opaque * ciphertext, + size_t ciphertext_size, content_type_t type, int random_pad) +{ + gnutls_datum_t plain; + gnutls_datum_t comp; + int ret; + int free_comp = 1; + + plain.data = (opaque *) data; + plain.size = data_size; + + if (plain.size == 0 || is_write_comp_null (session) == 0) + { + comp = plain; + free_comp = 0; + } + else + { + /* Here comp is allocated and must be + * freed. + */ + ret = _gnutls_m_plaintext2compressed (session, &comp, &plain); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + } + + ret = _gnutls_compressed2ciphertext (session, &ciphertext[headers_size], + ciphertext_size - headers_size, + comp, type, random_pad); + + if (free_comp) + _gnutls_free_datum (&comp); + + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + + /* copy the headers */ + memcpy (ciphertext, headers, headers_size); + _gnutls_write_uint16 (ret, &ciphertext[3]); + + return ret + headers_size; +} + +/* Decrypts the given data. + * Returns the decrypted data length. + */ +int +_gnutls_decrypt (gnutls_session_t session, opaque * ciphertext, + size_t ciphertext_size, uint8_t * data, + size_t max_data_size, content_type_t type) +{ + gnutls_datum_t gtxt; + gnutls_datum_t gcipher; + int ret; + + if (ciphertext_size == 0) + return 0; + + gcipher.size = ciphertext_size; + gcipher.data = ciphertext; + + ret = + _gnutls_ciphertext2compressed (session, data, max_data_size, + gcipher, type); + if (ret < 0) + { + return ret; + } + + if (ret == 0 || is_read_comp_null (session) == 0) + { + /* ret == ret */ + + } + else + { + gnutls_datum_t gcomp; + + /* compression has this malloc overhead. + */ + + gcomp.data = data; + gcomp.size = ret; + ret = _gnutls_m_compressed2plaintext (session, >xt, &gcomp); + if (ret < 0) + { + return ret; + } + + if (gtxt.size > MAX_RECORD_RECV_SIZE) + { + gnutls_assert (); + _gnutls_free_datum (>xt); + /* This shouldn't have happen and + * is a TLS fatal error. + */ + return GNUTLS_E_DECOMPRESSION_FAILED; + } + + /* This check is not really needed */ + if (max_data_size < MAX_RECORD_RECV_SIZE) + { + gnutls_assert (); + _gnutls_free_datum (>xt); + return GNUTLS_E_INTERNAL_ERROR; + } + + memcpy (data, gtxt.data, gtxt.size); + ret = gtxt.size; + + _gnutls_free_datum (>xt); + } + + return ret; +} + +inline static mac_hd_t +mac_init (gnutls_mac_algorithm_t mac, opaque * secret, int secret_size, + int ver) +{ + mac_hd_t td; + + if (mac == GNUTLS_MAC_NULL) + return GNUTLS_MAC_FAILED; + + if (ver == GNUTLS_SSL3) + { /* SSL 3.0 */ + td = _gnutls_mac_init_ssl3 (mac, secret, secret_size); + } + else + { /* TLS 1.x */ + td = _gnutls_hmac_init (mac, secret, secret_size); + } + + return td; +} + +inline static void +mac_deinit (mac_hd_t td, opaque * res, int ver) +{ + if (ver == GNUTLS_SSL3) + { /* SSL 3.0 */ + _gnutls_mac_deinit_ssl3 (td, res); + } + else + { + _gnutls_hmac_deinit (td, res); + } +} + +inline static int +calc_enc_length (gnutls_session_t session, int data_size, + int hash_size, uint8_t * pad, int random_pad, + cipher_type_t block_algo, uint16_t blocksize) +{ + uint8_t rnd; + int length; + + *pad = 0; + + switch (block_algo) + { + case CIPHER_STREAM: + length = data_size + hash_size; + + break; + case CIPHER_BLOCK: + if (gc_nonce (&rnd, 1) != GC_OK) + { + gnutls_assert (); + return GNUTLS_E_RANDOM_FAILED; + } + + /* make rnd a multiple of blocksize */ + if (session->security_parameters.version == GNUTLS_SSL3 || + random_pad == 0) + { + rnd = 0; + } + else + { + rnd = (rnd / blocksize) * blocksize; + /* added to avoid the case of pad calculated 0 + * seen below for pad calculation. + */ + if (rnd > blocksize) + rnd -= blocksize; + } + + length = data_size + hash_size; + + *pad = (uint8_t) (blocksize - (length % blocksize)) + rnd; + + length += *pad; + if (session->security_parameters.version >= GNUTLS_TLS1_1) + length += blocksize; /* for the IV */ + + break; + default: + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + return length; +} + +/* This is the actual encryption + * Encrypts the given compressed datum, and puts the result to cipher_data, + * which has cipher_size size. + * return the actual encrypted data length. + */ +int +_gnutls_compressed2ciphertext (gnutls_session_t session, + opaque * cipher_data, int cipher_size, + gnutls_datum_t compressed, + content_type_t _type, int random_pad) +{ + uint8_t MAC[MAX_HASH_SIZE]; + uint16_t c_length; + uint8_t pad; + int length, ret; + mac_hd_t td; + uint8_t type = _type; + uint8_t major, minor; + int hash_size = + _gnutls_hash_get_algo_len (session->security_parameters. + write_mac_algorithm); + gnutls_protocol_t ver; + int blocksize = + _gnutls_cipher_get_block_size (session->security_parameters. + write_bulk_cipher_algorithm); + cipher_type_t block_algo = + _gnutls_cipher_is_block (session->security_parameters. + write_bulk_cipher_algorithm); + opaque *data_ptr; + + + ver = gnutls_protocol_get_version (session); + minor = _gnutls_version_get_minor (ver); + major = _gnutls_version_get_major (ver); + + + /* Initialize MAC */ + td = mac_init (session->security_parameters.write_mac_algorithm, + session->connection_state.write_mac_secret.data, + session->connection_state.write_mac_secret.size, ver); + + if (td == GNUTLS_MAC_FAILED + && session->security_parameters.write_mac_algorithm != GNUTLS_MAC_NULL) + { + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + c_length = _gnutls_conv_uint16 (compressed.size); + + if (td != GNUTLS_MAC_FAILED) + { /* actually when the algorithm in not the NULL one */ + _gnutls_hmac (td, + UINT64DATA (session->connection_state. + write_sequence_number), 8); + + _gnutls_hmac (td, &type, 1); + if (ver >= GNUTLS_TLS1) + { /* TLS 1.0 or higher */ + _gnutls_hmac (td, &major, 1); + _gnutls_hmac (td, &minor, 1); + } + _gnutls_hmac (td, &c_length, 2); + _gnutls_hmac (td, compressed.data, compressed.size); + mac_deinit (td, MAC, ver); + } + + + /* Calculate the encrypted length (padding etc.) + */ + length = + calc_enc_length (session, compressed.size, hash_size, &pad, + random_pad, block_algo, blocksize); + if (length < 0) + { + gnutls_assert (); + return length; + } + + /* copy the encrypted data to cipher_data. + */ + if (cipher_size < length) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + data_ptr = cipher_data; + if (block_algo == CIPHER_BLOCK && + session->security_parameters.version >= GNUTLS_TLS1_1) + { + /* copy the random IV. + */ + if (gc_nonce (data_ptr, blocksize) != GC_OK) + { + gnutls_assert (); + return GNUTLS_E_RANDOM_FAILED; + } + data_ptr += blocksize; + } + + memcpy (data_ptr, compressed.data, compressed.size); + data_ptr += compressed.size; + + if (hash_size > 0) + { + memcpy (data_ptr, MAC, hash_size); + data_ptr += hash_size; + } + if (block_algo == CIPHER_BLOCK && pad > 0) + { + memset (data_ptr, pad - 1, pad); + } + + + /* Actual encryption (inplace). + */ + ret = _gnutls_cipher_encrypt (session->connection_state. + write_cipher_state, cipher_data, length); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + return length; +} + +/* Deciphers the ciphertext packet, and puts the result to compress_data, of compress_size. + * Returns the actual compressed packet size. + */ +int +_gnutls_ciphertext2compressed (gnutls_session_t session, + opaque * compress_data, + int compress_size, + gnutls_datum_t ciphertext, uint8_t type) +{ + uint8_t MAC[MAX_HASH_SIZE]; + uint16_t c_length; + uint8_t pad; + int length; + mac_hd_t td; + uint16_t blocksize; + int ret, i, pad_failed = 0; + uint8_t major, minor; + gnutls_protocol_t ver; + int hash_size = + _gnutls_hash_get_algo_len (session->security_parameters. + read_mac_algorithm); + + ver = gnutls_protocol_get_version (session); + minor = _gnutls_version_get_minor (ver); + major = _gnutls_version_get_major (ver); + + blocksize = _gnutls_cipher_get_block_size (session->security_parameters. + read_bulk_cipher_algorithm); + + /* initialize MAC + */ + td = mac_init (session->security_parameters.read_mac_algorithm, + session->connection_state.read_mac_secret.data, + session->connection_state.read_mac_secret.size, ver); + + if (td == GNUTLS_MAC_FAILED + && session->security_parameters.read_mac_algorithm != GNUTLS_MAC_NULL) + { + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + + /* actual decryption (inplace) + */ + switch (_gnutls_cipher_is_block + (session->security_parameters.read_bulk_cipher_algorithm)) + { + case CIPHER_STREAM: + if ((ret = _gnutls_cipher_decrypt (session->connection_state. + read_cipher_state, + ciphertext.data, + ciphertext.size)) < 0) + { + gnutls_assert (); + return ret; + } + + length = ciphertext.size - hash_size; + + break; + case CIPHER_BLOCK: + if ((ciphertext.size < blocksize) || (ciphertext.size % blocksize != 0)) + { + gnutls_assert (); + return GNUTLS_E_DECRYPTION_FAILED; + } + + if ((ret = _gnutls_cipher_decrypt (session->connection_state. + read_cipher_state, + ciphertext.data, + ciphertext.size)) < 0) + { + gnutls_assert (); + return ret; + } + + /* ignore the IV in TLS 1.1. + */ + if (session->security_parameters.version >= GNUTLS_TLS1_1) + { + ciphertext.size -= blocksize; + ciphertext.data += blocksize; + + if (ciphertext.size == 0) + { + gnutls_assert (); + return GNUTLS_E_DECRYPTION_FAILED; + } + } + + pad = ciphertext.data[ciphertext.size - 1] + 1; /* pad */ + + length = ciphertext.size - hash_size - pad; + + if (pad > ciphertext.size - hash_size) + { + gnutls_assert (); + /* We do not fail here. We check below for the + * the pad_failed. If zero means success. + */ + pad_failed = GNUTLS_E_DECRYPTION_FAILED; + } + + /* Check the pading bytes (TLS 1.x) + */ + if (ver >= GNUTLS_TLS1 && pad_failed == 0) + for (i = 2; i < pad; i++) + { + if (ciphertext.data[ciphertext.size - i] != + ciphertext.data[ciphertext.size - 1]) + pad_failed = GNUTLS_E_DECRYPTION_FAILED; + } + break; + default: + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + if (length < 0) + length = 0; + c_length = _gnutls_conv_uint16 ((uint16_t) length); + + /* Pass the type, version, length and compressed through + * MAC. + */ + if (td != GNUTLS_MAC_FAILED) + { + _gnutls_hmac (td, + UINT64DATA (session->connection_state. + read_sequence_number), 8); + + _gnutls_hmac (td, &type, 1); + if (ver >= GNUTLS_TLS1) + { /* TLS 1.x */ + _gnutls_hmac (td, &major, 1); + _gnutls_hmac (td, &minor, 1); + } + _gnutls_hmac (td, &c_length, 2); + + if (length > 0) + _gnutls_hmac (td, ciphertext.data, length); + + mac_deinit (td, MAC, ver); + } + + /* This one was introduced to avoid a timing attack against the TLS + * 1.0 protocol. + */ + if (pad_failed != 0) + return pad_failed; + + /* HMAC was not the same. + */ + if (memcmp (MAC, &ciphertext.data[length], hash_size) != 0) + { + gnutls_assert (); + return GNUTLS_E_DECRYPTION_FAILED; + } + + /* copy the decrypted stuff to compress_data. + */ + if (compress_size < length) + { + gnutls_assert (); + return GNUTLS_E_DECOMPRESSION_FAILED; + } + memcpy (compress_data, ciphertext.data, length); + + return length; +} diff --git a/src/daemon/https/tls/gnutls_cipher.h b/src/daemon/https/tls/gnutls_cipher.h new file mode 100644 index 00000000..0279e859 --- /dev/null +++ b/src/daemon/https/tls/gnutls_cipher.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +int _gnutls_encrypt (gnutls_session_t session, const opaque * headers, + size_t headers_size, const opaque * data, + size_t data_size, opaque * ciphertext, + size_t ciphertext_size, content_type_t type, + int random_pad); + +int _gnutls_decrypt (gnutls_session_t session, opaque * ciphertext, + size_t ciphertext_size, uint8_t * data, size_t data_size, + content_type_t type); +int _gnutls_compressed2ciphertext (gnutls_session_t session, + opaque * cipher_data, int cipher_size, + gnutls_datum_t compressed, + content_type_t _type, int random_pad); +int _gnutls_ciphertext2compressed (gnutls_session_t session, + opaque * compress_data, + int compress_size, + gnutls_datum_t ciphertext, uint8_t type); diff --git a/src/daemon/https/tls/gnutls_cipher_int.c b/src/daemon/https/tls/gnutls_cipher_int.c new file mode 100644 index 00000000..c9200e5b --- /dev/null +++ b/src/daemon/https/tls/gnutls_cipher_int.c @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2000, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +#include +#include +#include +#include + +cipher_hd_t +_gnutls_cipher_init (gnutls_cipher_algorithm_t cipher, + const gnutls_datum_t * key, const gnutls_datum_t * iv) +{ + cipher_hd_t ret = NULL; + int err = GC_INVALID_CIPHER; /* doesn't matter */ + + switch (cipher) + { + case GNUTLS_CIPHER_AES_128_CBC: + err = gc_cipher_open (GC_AES128, GC_CBC, &ret); + break; + + case GNUTLS_CIPHER_AES_256_CBC: + err = gc_cipher_open (GC_AES256, GC_CBC, &ret); + break; + + case GNUTLS_CIPHER_3DES_CBC: + err = gc_cipher_open (GC_3DES, GC_CBC, &ret); + break; + + case GNUTLS_CIPHER_DES_CBC: + err = gc_cipher_open (GC_DES, GC_CBC, &ret); + break; + + case GNUTLS_CIPHER_ARCFOUR_128: + err = gc_cipher_open (GC_ARCFOUR128, GC_STREAM, &ret); + break; + + case GNUTLS_CIPHER_ARCFOUR_40: + err = gc_cipher_open (GC_ARCFOUR40, GC_STREAM, &ret); + break; + + case GNUTLS_CIPHER_RC2_40_CBC: + err = gc_cipher_open (GC_ARCTWO40, GC_CBC, &ret); + break; + +#ifdef ENABLE_CAMELLIA + case GNUTLS_CIPHER_CAMELLIA_128_CBC: + err = gc_cipher_open (GC_CAMELLIA128, GC_CBC, &ret); + break; + + case GNUTLS_CIPHER_CAMELLIA_256_CBC: + err = gc_cipher_open (GC_CAMELLIA256, GC_CBC, &ret); + break; +#endif + + default: + return NULL; + } + + if (err == 0) + { + gc_cipher_setkey (ret, key->size, key->data); + if (iv->data != NULL && iv->size > 0) + gc_cipher_setiv (ret, iv->size, iv->data); + } + else if (cipher != GNUTLS_CIPHER_NULL) + { + gnutls_assert (); + _gnutls_x509_log ("Crypto cipher[%d] error: %d\n", cipher, err); + /* FIXME: gc_strerror */ + } + + return ret; +} + +int +_gnutls_cipher_encrypt (cipher_hd_t handle, void *text, int textlen) +{ + if (handle != GNUTLS_CIPHER_FAILED) + { + if (gc_cipher_encrypt_inline (handle, textlen, text) != 0) + { + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + } + return 0; +} + +int +_gnutls_cipher_decrypt (cipher_hd_t handle, void *ciphertext, + int ciphertextlen) +{ + if (handle != GNUTLS_CIPHER_FAILED) + { + if (gc_cipher_decrypt_inline (handle, ciphertextlen, ciphertext) != 0) + { + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + } + return 0; +} + +void +_gnutls_cipher_deinit (cipher_hd_t handle) +{ + if (handle != GNUTLS_CIPHER_FAILED) + { + gc_cipher_close (handle); + } +} diff --git a/src/daemon/https/tls/gnutls_cipher_int.h b/src/daemon/https/tls/gnutls_cipher_int.h new file mode 100644 index 00000000..78ed487f --- /dev/null +++ b/src/daemon/https/tls/gnutls_cipher_int.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +#ifndef GNUTLS_CIPHER_INT +# define GNUTLS_CIPHER_INT + +#define cipher_hd_t gc_cipher_handle +#define GNUTLS_CIPHER_FAILED NULL + +// TODO gc_cipher_handle -> void * x3 +void * _gnutls_cipher_init(gnutls_cipher_algorithm_t cipher, + const gnutls_datum_t * key, + const gnutls_datum_t * iv); + +int _gnutls_cipher_encrypt(void * handle, + void *text, + int textlen); + +int _gnutls_cipher_decrypt(void * handle, + void *ciphertext, + int ciphertextlen); + +void _gnutls_cipher_deinit(void * handle); + +#endif /* GNUTLS_CIPHER_INT */ diff --git a/src/daemon/https/tls/gnutls_compress.c b/src/daemon/https/tls/gnutls_compress.c new file mode 100644 index 00000000..e6561ad6 --- /dev/null +++ b/src/daemon/https/tls/gnutls_compress.c @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2000, 2004, 2005, 2007 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +/* This file contains the functions which convert the TLS plaintext + * packet to TLS compressed packet. + */ + +#include "gnutls_int.h" +#include "gnutls_compress.h" +#include "gnutls_errors.h" +#include "gnutls_compress_int.h" + +/* These functions allocate the return value internally + */ +int +_gnutls_m_plaintext2compressed (gnutls_session_t session, + gnutls_datum_t * compressed, + const gnutls_datum_t * plaintext) +{ + int size; + opaque *data; + + size = + _gnutls_compress (session->connection_state.write_compression_state, + plaintext->data, plaintext->size, &data, + MAX_RECORD_SEND_SIZE + EXTRA_COMP_SIZE); + if (size < 0) + { + gnutls_assert (); + return GNUTLS_E_COMPRESSION_FAILED; + } + compressed->data = data; + compressed->size = size; + + return 0; +} + +int +_gnutls_m_compressed2plaintext (gnutls_session_t session, + gnutls_datum_t * plain, + const gnutls_datum_t * compressed) +{ + int size; + opaque *data; + + size = + _gnutls_decompress (session->connection_state. + read_compression_state, compressed->data, + compressed->size, &data, MAX_RECORD_RECV_SIZE); + if (size < 0) + { + gnutls_assert (); + return GNUTLS_E_DECOMPRESSION_FAILED; + } + plain->data = data; + plain->size = size; + + return 0; +} diff --git a/src/daemon/https/tls/gnutls_compress.h b/src/daemon/https/tls/gnutls_compress.h new file mode 100644 index 00000000..44666321 --- /dev/null +++ b/src/daemon/https/tls/gnutls_compress.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +int _gnutls_m_plaintext2compressed (gnutls_session_t session, + gnutls_datum_t * compressed, + const gnutls_datum_t *plaintext); +int _gnutls_m_compressed2plaintext (gnutls_session_t session, + gnutls_datum_t * plain, + const gnutls_datum_t* compressed); diff --git a/src/daemon/https/tls/gnutls_compress_int.c b/src/daemon/https/tls/gnutls_compress_int.c new file mode 100644 index 00000000..eedfd63d --- /dev/null +++ b/src/daemon/https/tls/gnutls_compress_int.c @@ -0,0 +1,300 @@ +/* + * Copyright (C) 2000, 2002, 2003, 2004, 2005, 2007 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +#include +#include +#include +#include "gnutls_errors.h" + +/* The flag d is the direction (compress, decompress). Non zero is + * decompress. + */ +comp_hd_t +_gnutls_comp_init (gnutls_compression_method_t method, int d) +{ + comp_hd_t ret; + int err; + + ret = gnutls_malloc (sizeof (struct comp_hd_t_STRUCT)); + if (ret == NULL) + { + gnutls_assert (); + return NULL; + } + + ret->algo = method; + ret->handle = NULL; + + switch (method) + { +#ifdef HAVE_LIBZ + case GNUTLS_COMP_DEFLATE: + { + int window_bits, mem_level; + int comp_level; + z_stream *zhandle; + + window_bits = _gnutls_compression_get_wbits (method); + mem_level = _gnutls_compression_get_mem_level (method); + comp_level = _gnutls_compression_get_comp_level (method); + + ret->handle = gnutls_malloc (sizeof (z_stream)); + if (ret->handle == NULL) + { + gnutls_assert (); + goto cleanup_ret; + } + + zhandle = ret->handle; + + zhandle->zalloc = (alloc_func) 0; + zhandle->zfree = (free_func) 0; + zhandle->opaque = (voidpf) 0; + + if (d) + err = inflateInit2 (zhandle, window_bits); + else + { + err = deflateInit2 (zhandle, + comp_level, Z_DEFLATED, + window_bits, mem_level, Z_DEFAULT_STRATEGY); + } + if (err != Z_OK) + { + gnutls_assert (); + gnutls_free (ret->handle); + goto cleanup_ret; + } + break; + } +#endif + case GNUTLS_COMP_NULL: + break; + } + return ret; + +cleanup_ret: + gnutls_free (ret); + return NULL; +} + +/* The flag d is the direction (compress, decompress). Non zero is + * decompress. + */ +void +_gnutls_comp_deinit (comp_hd_t handle, int d) +{ + int err; + + if (handle != NULL) + { + switch (handle->algo) + { +#ifdef HAVE_LIBZ + case GNUTLS_COMP_DEFLATE: + if (d) + err = inflateEnd (handle->handle); + else + err = deflateEnd (handle->handle); + break; +#endif + default: + break; + } + gnutls_free (handle->handle); + gnutls_free (handle); + + } +} + +/* These functions are memory consuming + */ + +int +_gnutls_compress (comp_hd_t handle, const opaque * plain, + size_t plain_size, opaque ** compressed, + size_t max_comp_size) +{ + int compressed_size = GNUTLS_E_COMPRESSION_FAILED; + int err; + + /* NULL compression is not handled here + */ + if (handle == NULL) + { + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + switch (handle->algo) + { + +#ifdef HAVE_LIBZ + case GNUTLS_COMP_DEFLATE: + { + uLongf size; + z_stream *zhandle; + + size = (plain_size + plain_size) + 10; + *compressed = gnutls_malloc (size); + if (*compressed == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + zhandle = handle->handle; + + zhandle->next_in = (Bytef *) plain; + zhandle->avail_in = plain_size; + zhandle->next_out = (Bytef *) * compressed; + zhandle->avail_out = size; + + err = deflate (zhandle, Z_SYNC_FLUSH); + + if (err != Z_OK || zhandle->avail_in != 0) + { + gnutls_assert (); + gnutls_free (*compressed); + *compressed = NULL; + return GNUTLS_E_COMPRESSION_FAILED; + } + + compressed_size = size - zhandle->avail_out; + break; + } +#endif + default: + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } /* switch */ + +#ifdef COMPRESSION_DEBUG + _gnutls_debug_log ("Compression ratio: %f\n", + (float) ((float) compressed_size / (float) plain_size)); +#endif + + if ((size_t) compressed_size > max_comp_size) + { + gnutls_free (*compressed); + *compressed = NULL; + return GNUTLS_E_COMPRESSION_FAILED; + } + + return compressed_size; +} + + + +int +_gnutls_decompress (comp_hd_t handle, opaque * compressed, + size_t compressed_size, opaque ** plain, + size_t max_record_size) +{ + int plain_size = GNUTLS_E_DECOMPRESSION_FAILED, err; + int cur_pos; + + if (compressed_size > max_record_size + EXTRA_COMP_SIZE) + { + gnutls_assert (); + return GNUTLS_E_DECOMPRESSION_FAILED; + } + + /* NULL compression is not handled here + */ + + if (handle == NULL) + { + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + switch (handle->algo) + { +#ifdef HAVE_LIBZ + case GNUTLS_COMP_DEFLATE: + { + uLongf out_size; + z_stream *zhandle; + + *plain = NULL; + out_size = compressed_size + compressed_size; + plain_size = 0; + + zhandle = handle->handle; + + zhandle->next_in = (Bytef *) compressed; + zhandle->avail_in = compressed_size; + + cur_pos = 0; + + do + { + out_size += 512; + *plain = gnutls_realloc_fast (*plain, out_size); + if (*plain == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + zhandle->next_out = (Bytef *) (*plain + cur_pos); + zhandle->avail_out = out_size - cur_pos; + + err = inflate (zhandle, Z_SYNC_FLUSH); + + cur_pos = out_size - zhandle->avail_out; + + } + while ((err == Z_BUF_ERROR && zhandle->avail_out == 0 + && out_size < max_record_size) + || (err == Z_OK && zhandle->avail_in != 0)); + + if (err != Z_OK) + { + gnutls_assert (); + gnutls_free (*plain); + *plain = NULL; + return GNUTLS_E_DECOMPRESSION_FAILED; + } + + plain_size = out_size - zhandle->avail_out; + break; + } +#endif + default: + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } /* switch */ + + if ((size_t) plain_size > max_record_size) + { + gnutls_assert (); + gnutls_free (*plain); + *plain = NULL; + return GNUTLS_E_DECOMPRESSION_FAILED; + } + + return plain_size; +} diff --git a/src/daemon/https/tls/gnutls_compress_int.h b/src/daemon/https/tls/gnutls_compress_int.h new file mode 100644 index 00000000..4479fb4d --- /dev/null +++ b/src/daemon/https/tls/gnutls_compress_int.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +#ifndef GNUTLS_COMP_INT +# define GNUTLS_COMP_INT + +#ifdef HAVE_LIBZ +# include +#endif + +#define GNUTLS_COMP_FAILED NULL + +typedef struct comp_hd_t_STRUCT +{ + void *handle; + gnutls_compression_method_t algo; +} *comp_hd_t; + +comp_hd_t _gnutls_comp_init (gnutls_compression_method_t, int d); +void _gnutls_comp_deinit (comp_hd_t handle, int d); + +int _gnutls_decompress (comp_hd_t handle, opaque * compressed, + size_t compressed_size, opaque ** plain, + size_t max_record_size); +int _gnutls_compress (comp_hd_t, const opaque * plain, size_t plain_size, + opaque ** compressed, size_t max_comp_size); + +#endif diff --git a/src/daemon/https/tls/gnutls_constate.c b/src/daemon/https/tls/gnutls_constate.c new file mode 100644 index 00000000..c2323c46 --- /dev/null +++ b/src/daemon/https/tls/gnutls_constate.c @@ -0,0 +1,1039 @@ +/* + * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +/* Functions that are supposed to run after the handshake procedure is + * finished. These functions activate the established security parameters. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static const char keyexp[] = "key expansion"; +static const int keyexp_length = sizeof (keyexp) - 1; + +static const char ivblock[] = "IV block"; +static const int ivblock_length = sizeof (ivblock) - 1; + +static const char cliwrite[] = "client write key"; +static const int cliwrite_length = sizeof (cliwrite) - 1; + +static const char servwrite[] = "server write key"; +static const int servwrite_length = sizeof (servwrite) - 1; + +#define EXPORT_FINAL_KEY_SIZE 16 + +/* This function is to be called after handshake, when master_secret, + * client_random and server_random have been initialized. + * This function creates the keys and stores them into pending session. + * (session->cipher_specs) + */ +int +_gnutls_set_keys (gnutls_session_t session, int hash_size, int IV_size, + int key_size, int export_flag) +{ + +/* FIXME: This function is too long + */ + opaque *key_block; + opaque rnd[2 * TLS_RANDOM_SIZE]; + opaque rrnd[2 * TLS_RANDOM_SIZE]; + int pos, ret; + int block_size; + char buf[65]; + + if (session->cipher_specs.generated_keys != 0) + { + /* keys have already been generated. + * reset generated_keys and exit normally. + */ + session->cipher_specs.generated_keys = 0; + return 0; + } + + block_size = 2 * hash_size + 2 * key_size; + if (export_flag == 0) + block_size += 2 * IV_size; + + key_block = gnutls_secure_malloc (block_size); + if (key_block == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + memcpy (rnd, session->security_parameters.server_random, TLS_RANDOM_SIZE); + memcpy (&rnd[TLS_RANDOM_SIZE], + session->security_parameters.client_random, TLS_RANDOM_SIZE); + + memcpy (rrnd, session->security_parameters.client_random, TLS_RANDOM_SIZE); + memcpy (&rrnd[TLS_RANDOM_SIZE], + session->security_parameters.server_random, TLS_RANDOM_SIZE); + + if (session->security_parameters.version == GNUTLS_SSL3) + { /* SSL 3 */ + ret = + _gnutls_ssl3_generate_random (session-> + security_parameters. + master_secret, + TLS_MASTER_SIZE, rnd, + 2 * TLS_RANDOM_SIZE, + block_size, key_block); + } + else + { /* TLS 1.0 */ + ret = + _gnutls_PRF (session, session->security_parameters.master_secret, + TLS_MASTER_SIZE, keyexp, keyexp_length, + rnd, 2 * TLS_RANDOM_SIZE, block_size, key_block); + } + + if (ret < 0) + { + gnutls_assert (); + gnutls_free (key_block); + return ret; + } + + _gnutls_hard_log ("INT: KEY BLOCK[%d]: %s\n", block_size, + _gnutls_bin2hex (key_block, block_size, buf, + sizeof (buf))); + + pos = 0; + if (hash_size > 0) + { + if (_gnutls_sset_datum + (&session->cipher_specs.client_write_mac_secret, + &key_block[pos], hash_size) < 0) + { + gnutls_free (key_block); + return GNUTLS_E_MEMORY_ERROR; + } + pos += hash_size; + + if (_gnutls_sset_datum + (&session->cipher_specs.server_write_mac_secret, + &key_block[pos], hash_size) < 0) + { + gnutls_free (key_block); + return GNUTLS_E_MEMORY_ERROR; + } + pos += hash_size; + } + + if (key_size > 0) + { + opaque *client_write_key, *server_write_key; + int client_write_key_size, server_write_key_size; + int free_keys = 0; + + if (export_flag == 0) + { + client_write_key = &key_block[pos]; + client_write_key_size = key_size; + + pos += key_size; + + server_write_key = &key_block[pos]; + server_write_key_size = key_size; + + pos += key_size; + + } + else + { /* export */ + free_keys = 1; + + client_write_key = gnutls_secure_malloc (EXPORT_FINAL_KEY_SIZE); + if (client_write_key == NULL) + { + gnutls_assert (); + gnutls_free (key_block); + return GNUTLS_E_MEMORY_ERROR; + } + + server_write_key = gnutls_secure_malloc (EXPORT_FINAL_KEY_SIZE); + if (server_write_key == NULL) + { + gnutls_assert (); + gnutls_free (key_block); + gnutls_free (client_write_key); + return GNUTLS_E_MEMORY_ERROR; + } + + /* generate the final keys */ + + if (session->security_parameters.version == GNUTLS_SSL3) + { /* SSL 3 */ + ret = + _gnutls_ssl3_hash_md5 (&key_block[pos], + key_size, rrnd, + 2 * TLS_RANDOM_SIZE, + EXPORT_FINAL_KEY_SIZE, + client_write_key); + + } + else + { /* TLS 1.0 */ + ret = + _gnutls_PRF (session, &key_block[pos], key_size, + cliwrite, cliwrite_length, + rrnd, + 2 * TLS_RANDOM_SIZE, + EXPORT_FINAL_KEY_SIZE, client_write_key); + } + + if (ret < 0) + { + gnutls_assert (); + gnutls_free (key_block); + gnutls_free (server_write_key); + gnutls_free (client_write_key); + return ret; + } + + client_write_key_size = EXPORT_FINAL_KEY_SIZE; + pos += key_size; + + if (session->security_parameters.version == GNUTLS_SSL3) + { /* SSL 3 */ + ret = + _gnutls_ssl3_hash_md5 (&key_block[pos], key_size, + rnd, 2 * TLS_RANDOM_SIZE, + EXPORT_FINAL_KEY_SIZE, + server_write_key); + } + else + { /* TLS 1.0 */ + ret = + _gnutls_PRF (session, &key_block[pos], key_size, + servwrite, servwrite_length, + rrnd, 2 * TLS_RANDOM_SIZE, + EXPORT_FINAL_KEY_SIZE, server_write_key); + } + + if (ret < 0) + { + gnutls_assert (); + gnutls_free (key_block); + gnutls_free (server_write_key); + gnutls_free (client_write_key); + return ret; + } + + server_write_key_size = EXPORT_FINAL_KEY_SIZE; + pos += key_size; + } + + if (_gnutls_sset_datum + (&session->cipher_specs.client_write_key, + client_write_key, client_write_key_size) < 0) + { + gnutls_free (key_block); + gnutls_free (server_write_key); + gnutls_free (client_write_key); + return GNUTLS_E_MEMORY_ERROR; + } + _gnutls_hard_log ("INT: CLIENT WRITE KEY [%d]: %s\n", + client_write_key_size, + _gnutls_bin2hex (client_write_key, + client_write_key_size, buf, + sizeof (buf))); + + if (_gnutls_sset_datum + (&session->cipher_specs.server_write_key, + server_write_key, server_write_key_size) < 0) + { + gnutls_free (key_block); + gnutls_free (server_write_key); + gnutls_free (client_write_key); + return GNUTLS_E_MEMORY_ERROR; + } + + _gnutls_hard_log ("INT: SERVER WRITE KEY [%d]: %s\n", + server_write_key_size, + _gnutls_bin2hex (server_write_key, + server_write_key_size, buf, + sizeof (buf))); + + if (free_keys != 0) + { + gnutls_free (server_write_key); + gnutls_free (client_write_key); + } + } + + + /* IV generation in export and non export ciphers. + */ + if (IV_size > 0 && export_flag == 0) + { + if (_gnutls_sset_datum + (&session->cipher_specs.client_write_IV, &key_block[pos], + IV_size) < 0) + { + gnutls_free (key_block); + return GNUTLS_E_MEMORY_ERROR; + } + pos += IV_size; + + if (_gnutls_sset_datum + (&session->cipher_specs.server_write_IV, &key_block[pos], + IV_size) < 0) + { + gnutls_free (key_block); + return GNUTLS_E_MEMORY_ERROR; + } + pos += IV_size; + + } + else if (IV_size > 0 && export_flag != 0) + { + opaque *iv_block = gnutls_alloca (IV_size * 2); + if (iv_block == NULL) + { + gnutls_assert (); + gnutls_free (key_block); + return GNUTLS_E_MEMORY_ERROR; + } + + if (session->security_parameters.version == GNUTLS_SSL3) + { /* SSL 3 */ + ret = _gnutls_ssl3_hash_md5 ("", 0, + rrnd, TLS_RANDOM_SIZE * 2, + IV_size, iv_block); + + if (ret < 0) + { + gnutls_assert (); + gnutls_free (key_block); + gnutls_afree (iv_block); + return ret; + } + + ret = _gnutls_ssl3_hash_md5 ("", 0, rnd, + TLS_RANDOM_SIZE * 2, + IV_size, &iv_block[IV_size]); + + } + else + { /* TLS 1.0 */ + ret = _gnutls_PRF (session, "", 0, + ivblock, ivblock_length, rrnd, + 2 * TLS_RANDOM_SIZE, IV_size * 2, iv_block); + } + + if (ret < 0) + { + gnutls_assert (); + gnutls_afree (iv_block); + gnutls_free (key_block); + return ret; + } + + if (_gnutls_sset_datum + (&session->cipher_specs.client_write_IV, iv_block, IV_size) < 0) + { + gnutls_afree (iv_block); + gnutls_free (key_block); + return GNUTLS_E_MEMORY_ERROR; + } + + if (_gnutls_sset_datum + (&session->cipher_specs.server_write_IV, + &iv_block[IV_size], IV_size) < 0) + { + gnutls_afree (iv_block); + gnutls_free (key_block); + return GNUTLS_E_MEMORY_ERROR; + } + + gnutls_afree (iv_block); + } + + gnutls_free (key_block); + + session->cipher_specs.generated_keys = 1; + + return 0; +} + +int +_gnutls_set_read_keys (gnutls_session_t session) +{ + int hash_size; + int IV_size; + int key_size, export_flag; + gnutls_cipher_algorithm_t algo; + gnutls_mac_algorithm_t mac_algo; + + mac_algo = session->security_parameters.read_mac_algorithm; + algo = session->security_parameters.read_bulk_cipher_algorithm; + + hash_size = _gnutls_hash_get_algo_len (mac_algo); + IV_size = _gnutls_cipher_get_iv_size (algo); + key_size = gnutls_cipher_get_key_size (algo); + export_flag = _gnutls_cipher_get_export_flag (algo); + + return _gnutls_set_keys (session, hash_size, IV_size, key_size, + export_flag); +} + +int +_gnutls_set_write_keys (gnutls_session_t session) +{ + int hash_size; + int IV_size; + int key_size, export_flag; + gnutls_cipher_algorithm_t algo; + gnutls_mac_algorithm_t mac_algo; + + mac_algo = session->security_parameters.write_mac_algorithm; + algo = session->security_parameters.write_bulk_cipher_algorithm; + + hash_size = _gnutls_hash_get_algo_len (mac_algo); + IV_size = _gnutls_cipher_get_iv_size (algo); + key_size = gnutls_cipher_get_key_size (algo); + export_flag = _gnutls_cipher_get_export_flag (algo); + + return _gnutls_set_keys (session, hash_size, IV_size, key_size, + export_flag); +} + +#define CPY_COMMON dst->entity = src->entity; \ + dst->kx_algorithm = src->kx_algorithm; \ + memcpy( &dst->current_cipher_suite, &src->current_cipher_suite, sizeof(cipher_suite_st)); \ + memcpy( dst->master_secret, src->master_secret, TLS_MASTER_SIZE); \ + memcpy( dst->client_random, src->client_random, TLS_RANDOM_SIZE); \ + memcpy( dst->server_random, src->server_random, TLS_RANDOM_SIZE); \ + memcpy( dst->session_id, src->session_id, TLS_MAX_SESSION_ID_SIZE); \ + dst->session_id_size = src->session_id_size; \ + dst->cert_type = src->cert_type; \ + dst->timestamp = src->timestamp; \ + dst->max_record_recv_size = src->max_record_recv_size; \ + dst->max_record_send_size = src->max_record_send_size; \ + dst->version = src->version; \ + memcpy( &dst->extensions, &src->extensions, sizeof(tls_ext_st)); \ + memcpy( &dst->inner_secret, &src->inner_secret, TLS_MASTER_SIZE); + +static void +_gnutls_cpy_read_security_parameters (security_parameters_st * + dst, security_parameters_st * src) +{ + CPY_COMMON; + + dst->read_bulk_cipher_algorithm = src->read_bulk_cipher_algorithm; + dst->read_mac_algorithm = src->read_mac_algorithm; + dst->read_compression_algorithm = src->read_compression_algorithm; +} + +static void +_gnutls_cpy_write_security_parameters (security_parameters_st * + dst, security_parameters_st * src) +{ + CPY_COMMON; + + dst->write_bulk_cipher_algorithm = src->write_bulk_cipher_algorithm; + dst->write_mac_algorithm = src->write_mac_algorithm; + dst->write_compression_algorithm = src->write_compression_algorithm; +} + +/* Sets the current connection session to conform with the + * Security parameters(pending session), and initializes encryption. + * Actually it initializes and starts encryption ( so it needs + * secrets and random numbers to have been negotiated) + * This is to be called after sending the Change Cipher Spec packet. + */ +int +_gnutls_connection_state_init (gnutls_session_t session) +{ + int ret; + +/* Setup the master secret + */ + if ((ret = _gnutls_generate_master (session, 0), 0) < 0) + { + gnutls_assert (); + return ret; + } + + + return 0; +} + + +/* Initializes the read connection session + * (read encrypted data) + */ +int +_gnutls_read_connection_state_init (gnutls_session_t session) +{ + int mac_size; + int rc; + + _gnutls_uint64zero (session->connection_state.read_sequence_number); + +/* Update internals from CipherSuite selected. + * If we are resuming just copy the connection session + */ + if (session->internals.resumed == RESUME_FALSE) + { + rc = _gnutls_set_read_cipher (session, + _gnutls_cipher_suite_get_cipher_algo + (&session->security_parameters. + current_cipher_suite)); + if (rc < 0) + return rc; + rc = _gnutls_set_read_mac (session, + _gnutls_cipher_suite_get_mac_algo + (&session->security_parameters. + current_cipher_suite)); + if (rc < 0) + return rc; + + rc = _gnutls_set_kx (session, + _gnutls_cipher_suite_get_kx_algo + (&session->security_parameters. + current_cipher_suite)); + if (rc < 0) + return rc; + + rc = _gnutls_set_read_compression (session, + session->internals. + compression_method); + if (rc < 0) + return rc; + } + else + { /* RESUME_TRUE */ + _gnutls_cpy_read_security_parameters (&session-> + security_parameters, + &session-> + internals. + resumed_security_parameters); + } + + + rc = _gnutls_set_read_keys (session); + if (rc < 0) + return rc; + + _gnutls_handshake_log ("HSK[%x]: Cipher Suite: %s\n", + session, _gnutls_cipher_suite_get_name (&session-> + security_parameters. + current_cipher_suite)); + + if (_gnutls_compression_is_ok + (session->security_parameters.read_compression_algorithm) != 0) + { + gnutls_assert (); + return GNUTLS_E_UNKNOWN_COMPRESSION_ALGORITHM; + } + + if (_gnutls_mac_is_ok + (session->security_parameters.read_mac_algorithm) != 0) + { + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + /* Free all the previous keys/ sessions etc. + */ + if (session->connection_state.read_mac_secret.data != NULL) + _gnutls_free_datum (&session->connection_state.read_mac_secret); + + if (session->connection_state.read_cipher_state != NULL) + _gnutls_cipher_deinit (session->connection_state.read_cipher_state); + + if (session->connection_state.read_compression_state != NULL) + _gnutls_comp_deinit (session->connection_state.read_compression_state, 1); + + + mac_size = + _gnutls_hash_get_algo_len (session->security_parameters. + read_mac_algorithm); + + _gnutls_handshake_log + ("HSK[%x]: Initializing internal [read] cipher sessions\n", session); + + switch (session->security_parameters.entity) + { + case GNUTLS_SERVER: + /* initialize cipher session + */ + session->connection_state.read_cipher_state = + _gnutls_cipher_init (session->security_parameters. + read_bulk_cipher_algorithm, + &session->cipher_specs. + client_write_key, + &session->cipher_specs.client_write_IV); + if (session->connection_state.read_cipher_state == + GNUTLS_CIPHER_FAILED + && session->security_parameters. + read_bulk_cipher_algorithm != GNUTLS_CIPHER_NULL) + { + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + /* copy mac secrets from cipherspecs, to connection + * session. + */ + if (mac_size > 0) + { + if (_gnutls_sset_datum (&session->connection_state. + read_mac_secret, + session->cipher_specs. + client_write_mac_secret.data, + session->cipher_specs. + client_write_mac_secret.size) < 0) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + } + + break; + + case GNUTLS_CLIENT: + session->connection_state.read_cipher_state = + _gnutls_cipher_init (session->security_parameters. + read_bulk_cipher_algorithm, + &session->cipher_specs. + server_write_key, + &session->cipher_specs.server_write_IV); + + if (session->connection_state.read_cipher_state == + GNUTLS_CIPHER_FAILED + && session->security_parameters. + read_bulk_cipher_algorithm != GNUTLS_CIPHER_NULL) + { + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + + /* copy mac secret to connection session + */ + if (mac_size > 0) + { + if (_gnutls_sset_datum (&session->connection_state. + read_mac_secret, + session->cipher_specs. + server_write_mac_secret.data, + session->cipher_specs. + server_write_mac_secret.size) < 0) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + } + + break; + + default: /* this check is useless */ + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + session->connection_state.read_compression_state = + _gnutls_comp_init (session->security_parameters. + read_compression_algorithm, 1); + + if (session->connection_state.read_compression_state == GNUTLS_COMP_FAILED) + { + gnutls_assert (); + return GNUTLS_E_UNKNOWN_COMPRESSION_ALGORITHM; + } + + return 0; +} + + + +/* Initializes the write connection session + * (write encrypted data) + */ +int +_gnutls_write_connection_state_init (gnutls_session_t session) +{ + int mac_size; + int rc; + + _gnutls_uint64zero (session->connection_state.write_sequence_number); + +/* Update internals from CipherSuite selected. + * If we are resuming just copy the connection session + */ + if (session->internals.resumed == RESUME_FALSE) + { + rc = _gnutls_set_write_cipher (session, + _gnutls_cipher_suite_get_cipher_algo + (&session->security_parameters. + current_cipher_suite)); + if (rc < 0) + return rc; + rc = _gnutls_set_write_mac (session, + _gnutls_cipher_suite_get_mac_algo + (&session->security_parameters. + current_cipher_suite)); + if (rc < 0) + return rc; + + rc = _gnutls_set_kx (session, + _gnutls_cipher_suite_get_kx_algo + (&session->security_parameters. + current_cipher_suite)); + if (rc < 0) + return rc; + + rc = _gnutls_set_write_compression (session, + session->internals. + compression_method); + if (rc < 0) + return rc; + } + else + { /* RESUME_TRUE */ + _gnutls_cpy_write_security_parameters (&session-> + security_parameters, + &session-> + internals. + resumed_security_parameters); + } + + rc = _gnutls_set_write_keys (session); + if (rc < 0) + return rc; + + _gnutls_handshake_log ("HSK[%x]: Cipher Suite: %s\n", session, + _gnutls_cipher_suite_get_name (&session-> + security_parameters. + current_cipher_suite)); + + if (_gnutls_compression_is_ok + (session->security_parameters.write_compression_algorithm) != 0) + { + gnutls_assert (); + return GNUTLS_E_UNKNOWN_COMPRESSION_ALGORITHM; + } + + if (_gnutls_mac_is_ok + (session->security_parameters.write_mac_algorithm) != 0) + { + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + + + /* Free all the previous keys/ sessions etc. + */ + if (session->connection_state.write_mac_secret.data != NULL) + _gnutls_free_datum (&session->connection_state.write_mac_secret); + + if (session->connection_state.write_cipher_state != NULL) + _gnutls_cipher_deinit (session->connection_state.write_cipher_state); + + if (session->connection_state.write_compression_state != NULL) + _gnutls_comp_deinit (session->connection_state. + write_compression_state, 0); + + mac_size = + _gnutls_hash_get_algo_len (session->security_parameters. + write_mac_algorithm); + + _gnutls_handshake_log + ("HSK[%x]: Initializing internal [write] cipher sessions\n", session); + + switch (session->security_parameters.entity) + { + case GNUTLS_SERVER: + /* initialize cipher session + */ + session->connection_state.write_cipher_state = + _gnutls_cipher_init (session->security_parameters. + write_bulk_cipher_algorithm, + &session->cipher_specs. + server_write_key, + &session->cipher_specs.server_write_IV); + + if (session->connection_state.write_cipher_state == + GNUTLS_CIPHER_FAILED + && session->security_parameters. + write_bulk_cipher_algorithm != GNUTLS_CIPHER_NULL) + { + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + + /* copy mac secrets from cipherspecs, to connection + * session. + */ + if (mac_size > 0) + { + if (_gnutls_sset_datum (&session->connection_state. + write_mac_secret, + session->cipher_specs. + server_write_mac_secret.data, + session->cipher_specs. + server_write_mac_secret.size) < 0) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + } + + + break; + + case GNUTLS_CLIENT: + session->connection_state.write_cipher_state = + _gnutls_cipher_init (session->security_parameters. + write_bulk_cipher_algorithm, + &session->cipher_specs. + client_write_key, + &session->cipher_specs.client_write_IV); + + if (session->connection_state.write_cipher_state == + GNUTLS_CIPHER_FAILED + && session->security_parameters. + write_bulk_cipher_algorithm != GNUTLS_CIPHER_NULL) + { + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + /* copy mac secret to connection session + */ + if (mac_size > 0) + { + if (_gnutls_sset_datum (&session->connection_state. + write_mac_secret, + session->cipher_specs. + client_write_mac_secret.data, + session->cipher_specs. + client_write_mac_secret.size) < 0) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + } + + break; + + default: + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + + session->connection_state.write_compression_state = + _gnutls_comp_init (session->security_parameters. + write_compression_algorithm, 0); + + if (session->connection_state.write_compression_state == GNUTLS_COMP_FAILED) + { + gnutls_assert (); + return GNUTLS_E_UNKNOWN_COMPRESSION_ALGORITHM; + } + + return 0; +} + +/* Sets the specified cipher into the pending session + */ +int +_gnutls_set_read_cipher (gnutls_session_t session, + gnutls_cipher_algorithm_t algo) +{ + + if (_gnutls_cipher_is_ok (algo) == 0) + { + if (_gnutls_cipher_priority (session, algo) < 0) + { + gnutls_assert (); + return GNUTLS_E_UNWANTED_ALGORITHM; + } + + session->security_parameters.read_bulk_cipher_algorithm = algo; + + } + else + { + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + return 0; + +} + +int +_gnutls_set_write_cipher (gnutls_session_t session, + gnutls_cipher_algorithm_t algo) +{ + + if (_gnutls_cipher_is_ok (algo) == 0) + { + if (_gnutls_cipher_priority (session, algo) < 0) + { + gnutls_assert (); + return GNUTLS_E_UNWANTED_ALGORITHM; + } + + session->security_parameters.write_bulk_cipher_algorithm = algo; + + } + else + { + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + return 0; + +} + + +/* Sets the specified algorithm into pending compression session + */ +int +_gnutls_set_read_compression (gnutls_session_t session, + gnutls_compression_method_t algo) +{ + + if (_gnutls_compression_is_ok (algo) == 0) + { + session->security_parameters.read_compression_algorithm = algo; + } + else + { + gnutls_assert (); + return GNUTLS_E_UNKNOWN_COMPRESSION_ALGORITHM; + } + return 0; + +} + +int +_gnutls_set_write_compression (gnutls_session_t session, + gnutls_compression_method_t algo) +{ + + if (_gnutls_compression_is_ok (algo) == 0) + { + session->security_parameters.write_compression_algorithm = algo; + } + else + { + gnutls_assert (); + return GNUTLS_E_UNKNOWN_COMPRESSION_ALGORITHM; + } + return 0; + +} + +/* Sets the specified kx algorithm into pending session + */ +int +_gnutls_set_kx (gnutls_session_t session, gnutls_kx_algorithm_t algo) +{ + + if (_gnutls_kx_is_ok (algo) == 0) + { + session->security_parameters.kx_algorithm = algo; + } + else + { + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + if (_gnutls_kx_priority (session, algo) < 0) + { + gnutls_assert (); + /* we shouldn't get here */ + return GNUTLS_E_UNWANTED_ALGORITHM; + } + + return 0; + +} + +/* Sets the specified mac algorithm into pending session */ +int +_gnutls_set_read_mac (gnutls_session_t session, gnutls_mac_algorithm_t algo) +{ + + if (_gnutls_mac_is_ok (algo) == 0) + { + session->security_parameters.read_mac_algorithm = algo; + } + else + { + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + if (_gnutls_mac_priority (session, algo) < 0) + { + gnutls_assert (); + return GNUTLS_E_UNWANTED_ALGORITHM; + } + + + return 0; + +} + +int +_gnutls_set_write_mac (gnutls_session_t session, gnutls_mac_algorithm_t algo) +{ + + if (_gnutls_mac_is_ok (algo) == 0) + { + session->security_parameters.write_mac_algorithm = algo; + } + else + { + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + if (_gnutls_mac_priority (session, algo) < 0) + { + gnutls_assert (); + return GNUTLS_E_UNWANTED_ALGORITHM; + } + + + return 0; + +} diff --git a/src/daemon/https/tls/gnutls_constate.h b/src/daemon/https/tls/gnutls_constate.h new file mode 100644 index 00000000..f58c8b14 --- /dev/null +++ b/src/daemon/https/tls/gnutls_constate.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +int _gnutls_connection_state_init (gnutls_session_t session); +int _gnutls_read_connection_state_init (gnutls_session_t session); +int _gnutls_write_connection_state_init (gnutls_session_t session); +int _gnutls_set_write_cipher (gnutls_session_t session, + gnutls_cipher_algorithm_t algo); +int _gnutls_set_write_mac (gnutls_session_t session, + gnutls_mac_algorithm_t algo); +int _gnutls_set_read_cipher (gnutls_session_t session, + gnutls_cipher_algorithm_t algo); +int _gnutls_set_read_mac (gnutls_session_t session, + gnutls_mac_algorithm_t algo); +int _gnutls_set_read_compression (gnutls_session_t session, + gnutls_compression_method_t algo); +int _gnutls_set_write_compression (gnutls_session_t session, + gnutls_compression_method_t algo); +int _gnutls_set_kx (gnutls_session_t session, gnutls_kx_algorithm_t algo); diff --git a/src/daemon/https/tls/gnutls_datum.c b/src/daemon/https/tls/gnutls_datum.c new file mode 100644 index 00000000..4b2eb1f4 --- /dev/null +++ b/src/daemon/https/tls/gnutls_datum.c @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +/* contains functions that make it easier to + * write vectors of . The destination size + * should be preallocated (datum.size+(bits/8)) + */ + +#include +#include +#include +#include + + +void +_gnutls_write_datum16 (opaque * dest, gnutls_datum_t dat) +{ + _gnutls_write_uint16 (dat.size, dest); + if (dat.data != NULL) + memcpy (&dest[2], dat.data, dat.size); +} + +void +_gnutls_write_datum24 (opaque * dest, gnutls_datum_t dat) +{ + _gnutls_write_uint24 (dat.size, dest); + if (dat.data != NULL) + memcpy (&dest[3], dat.data, dat.size); +} + +void +_gnutls_write_datum32 (opaque * dest, gnutls_datum_t dat) +{ + _gnutls_write_uint32 (dat.size, dest); + if (dat.data != NULL) + memcpy (&dest[4], dat.data, dat.size); +} + +void +_gnutls_write_datum8 (opaque * dest, gnutls_datum_t dat) +{ + dest[0] = (uint8_t) dat.size; + if (dat.data != NULL) + memcpy (&dest[1], dat.data, dat.size); +} + + +int +_gnutls_set_datum_m (gnutls_datum_t * dat, const void *data, + size_t data_size, gnutls_alloc_function galloc_func) +{ + if (data_size == 0 || data == NULL) + { + dat->data = NULL; + dat->size = 0; + return 0; + } + + dat->data = galloc_func (data_size); + if (dat->data == NULL) + return GNUTLS_E_MEMORY_ERROR; + + dat->size = data_size; + memcpy (dat->data, data, data_size); + + return 0; +} + +int +_gnutls_datum_append_m (gnutls_datum_t * dst, const void *data, + size_t data_size, + gnutls_realloc_function grealloc_func) +{ + + dst->data = grealloc_func (dst->data, data_size + dst->size); + if (dst->data == NULL) + return GNUTLS_E_MEMORY_ERROR; + + memcpy (&dst->data[dst->size], data, data_size); + dst->size += data_size; + + return 0; +} + +void +_gnutls_free_datum_m (gnutls_datum_t * dat, gnutls_free_function gfree_func) +{ + if (dat->data != NULL) + gfree_func (dat->data); + + dat->data = NULL; + dat->size = 0; +} diff --git a/src/daemon/https/tls/gnutls_datum.h b/src/daemon/https/tls/gnutls_datum.h new file mode 100644 index 00000000..0f609531 --- /dev/null +++ b/src/daemon/https/tls/gnutls_datum.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +void _gnutls_write_datum16 (opaque * dest, gnutls_datum_t dat); +void _gnutls_write_datum24 (opaque * dest, gnutls_datum_t dat); +void _gnutls_write_datum32 (opaque * dest, gnutls_datum_t dat); +void _gnutls_write_datum8 (opaque * dest, gnutls_datum_t dat); + +int _gnutls_set_datum_m (gnutls_datum_t * dat, const void *data, + size_t data_size, gnutls_alloc_function); +#define _gnutls_set_datum( x, y, z) _gnutls_set_datum_m(x,y,z, gnutls_malloc) +#define _gnutls_sset_datum( x, y, z) _gnutls_set_datum_m(x,y,z, gnutls_secure_malloc) + +int _gnutls_datum_append_m (gnutls_datum_t * dat, const void *data, + size_t data_size, gnutls_realloc_function); +#define _gnutls_datum_append(x,y,z) _gnutls_datum_append_m(x,y,z, gnutls_realloc) + +void _gnutls_free_datum_m (gnutls_datum_t * dat, gnutls_free_function); +#define _gnutls_free_datum(x) _gnutls_free_datum_m(x, gnutls_free) diff --git a/src/daemon/https/tls/gnutls_db.c b/src/daemon/https/tls/gnutls_db.c new file mode 100644 index 00000000..701be2f0 --- /dev/null +++ b/src/daemon/https/tls/gnutls_db.c @@ -0,0 +1,386 @@ +/* + * Copyright (C) 2000, 2002, 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +/* This file contains functions that manipulate a database backend + * for resumed sessions. + */ + +#include "gnutls_int.h" +#include "gnutls_errors.h" +#include "gnutls_session.h" +#include +#include "debug.h" +#include +#include + +/** + * gnutls_db_set_retrieve_function - Sets the function that will be used to get data + * @session: is a #gnutls_session_t structure. + * @retr_func: is the function. + * + * Sets the function that will be used to retrieve data from the resumed + * sessions database. This function must return a gnutls_datum_t containing the + * data on success, or a gnutls_datum_t containing null and 0 on failure. + * + * The datum's data must be allocated using the function + * gnutls_malloc(). + * + * The first argument to retr_func() will be null unless gnutls_db_set_ptr() + * has been called. + * + **/ +void +gnutls_db_set_retrieve_function (gnutls_session_t session, + gnutls_db_retr_func retr_func) +{ + session->internals.db_retrieve_func = retr_func; +} + +/** + * gnutls_db_set_remove_function - Sets the function that will be used to remove data + * @session: is a #gnutls_session_t structure. + * @rem_func: is the function. + * + * Sets the function that will be used to remove data from the resumed + * sessions database. This function must return 0 on success. + * + * The first argument to rem_func() will be null unless gnutls_db_set_ptr() + * has been called. + * + **/ +void +gnutls_db_set_remove_function (gnutls_session_t session, + gnutls_db_remove_func rem_func) +{ + session->internals.db_remove_func = rem_func; +} + +/** + * gnutls_db_set_store_function - Sets the function that will be used to put data + * @session: is a #gnutls_session_t structure. + * @store_func: is the function + * + * Sets the function that will be used to store data from the resumed + * sessions database. This function must remove 0 on success. + * + * The first argument to store_func() will be null unless gnutls_db_set_ptr() + * has been called. + * + **/ +void +gnutls_db_set_store_function (gnutls_session_t session, + gnutls_db_store_func store_func) +{ + session->internals.db_store_func = store_func; +} + +/** + * gnutls_db_set_ptr - Sets a pointer to be sent to db functions + * @session: is a #gnutls_session_t structure. + * @ptr: is the pointer + * + * Sets the pointer that will be provided to db store, retrieve and delete functions, as + * the first argument. + * + **/ +void +gnutls_db_set_ptr (gnutls_session_t session, void *ptr) +{ + session->internals.db_ptr = ptr; +} + +/** + * gnutls_db_get_ptr - Returns the pointer which is sent to db functions + * @session: is a #gnutls_session_t structure. + * + * Returns the pointer that will be sent to db store, retrieve and delete functions, as + * the first argument. + * + **/ +void * +gnutls_db_get_ptr (gnutls_session_t session) +{ + return session->internals.db_ptr; +} + +/** + * gnutls_db_set_cache_expiration - Sets the expiration time for resumed sessions. + * @session: is a #gnutls_session_t structure. + * @seconds: is the number of seconds. + * + * Sets the expiration time for resumed sessions. The default is 3600 (one hour) + * at the time writing this. + **/ +void +gnutls_db_set_cache_expiration (gnutls_session_t session, int seconds) +{ + session->internals.expire_time = seconds; +} + +/** + * gnutls_db_check_entry - checks if the given db entry has expired + * @session: is a #gnutls_session_t structure. + * @session_entry: is the session data (not key) + * + * This function returns GNUTLS_E_EXPIRED, if the database entry + * has expired or 0 otherwise. This function is to be used when + * you want to clear unnesessary session which occupy space in your + * backend. + * + **/ +int +gnutls_db_check_entry (gnutls_session_t session, gnutls_datum_t session_entry) +{ + time_t timestamp; + + timestamp = time (0); + + if (session_entry.data != NULL) + if (timestamp - + ((security_parameters_st *) (session_entry.data))->timestamp <= + session->internals.expire_time + || ((security_parameters_st *) (session_entry.data))-> + timestamp > timestamp + || ((security_parameters_st *) (session_entry.data))->timestamp == 0) + return GNUTLS_E_EXPIRED; + + return 0; +} + +/* The format of storing data is: + * (forget it). Check gnutls_session_pack.c + */ +int +_gnutls_server_register_current_session (gnutls_session_t session) +{ + gnutls_datum_t key; + gnutls_datum_t content; + int ret = 0; + + key.data = session->security_parameters.session_id; + key.size = session->security_parameters.session_id_size; + + if (session->internals.resumable == RESUME_FALSE) + { + gnutls_assert (); + return GNUTLS_E_INVALID_SESSION; + } + + if (session->security_parameters.session_id == NULL + || session->security_parameters.session_id_size == 0) + { + gnutls_assert (); + return GNUTLS_E_INVALID_SESSION; + } + +/* copy data */ + ret = _gnutls_session_pack (session, &content); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + ret = _gnutls_store_session (session, key, content); + _gnutls_free_datum (&content); + + return ret; +} + +/* Checks if both db_store and db_retrieve functions have + * been set up. + */ +static int +_gnutls_db_func_is_ok (gnutls_session_t session) +{ + if (session->internals.db_store_func != NULL && + session->internals.db_retrieve_func != NULL && + session->internals.db_remove_func != NULL) + return 0; + else + return GNUTLS_E_DB_ERROR; +} + + +int +_gnutls_server_restore_session (gnutls_session_t session, + uint8_t * session_id, int session_id_size) +{ + gnutls_datum_t data; + gnutls_datum_t key; + int ret; + + key.data = session_id; + key.size = session_id_size; + + if (_gnutls_db_func_is_ok (session) != 0) + { + gnutls_assert (); + return GNUTLS_E_INVALID_SESSION; + } + + data = _gnutls_retrieve_session (session, key); + + if (data.data == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_SESSION; + } + + /* expiration check is performed inside */ + ret = gnutls_session_set_data (session, data.data, data.size); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + gnutls_free (data.data); + + return 0; +} + +int +_gnutls_db_remove_session (gnutls_session_t session, uint8_t * session_id, + int session_id_size) +{ + gnutls_datum_t key; + + key.data = session_id; + key.size = session_id_size; + + return _gnutls_remove_session (session, key); +} + + +/* Stores session data to the db backend. + */ +int +_gnutls_store_session (gnutls_session_t session, + gnutls_datum_t session_id, gnutls_datum_t session_data) +{ + int ret = 0; + + if (session->internals.resumable == RESUME_FALSE) + { + gnutls_assert (); + return GNUTLS_E_INVALID_SESSION; + } + + if (_gnutls_db_func_is_ok (session) != 0) + { + return GNUTLS_E_DB_ERROR; + } + + if (session_id.data == NULL || session_id.size == 0) + { + gnutls_assert (); + return GNUTLS_E_INVALID_SESSION; + } + + if (session_data.data == NULL || session_data.size == 0) + { + gnutls_assert (); + return GNUTLS_E_INVALID_SESSION; + } + /* if we can't read why bother writing? */ + + if (session->internals.db_store_func != NULL) + ret = + session->internals.db_store_func (session->internals.db_ptr, + session_id, session_data); + + return (ret == 0 ? ret : GNUTLS_E_DB_ERROR); + +} + +/* Retrieves session data from the db backend. + */ +gnutls_datum_t +_gnutls_retrieve_session (gnutls_session_t session, gnutls_datum_t session_id) +{ + gnutls_datum_t ret = { NULL, 0 }; + + if (session_id.data == NULL || session_id.size == 0) + { + gnutls_assert (); + return ret; + } + + if (session->internals.db_retrieve_func != NULL) + ret = + session->internals.db_retrieve_func (session->internals.db_ptr, + session_id); + + return ret; + +} + +/* Removes session data from the db backend. + */ +int +_gnutls_remove_session (gnutls_session_t session, gnutls_datum_t session_id) +{ + int ret = 0; + + if (_gnutls_db_func_is_ok (session) != 0) + { + return GNUTLS_E_DB_ERROR; + } + + if (session_id.data == NULL || session_id.size == 0) + return GNUTLS_E_INVALID_SESSION; + + /* if we can't read why bother writing? */ + if (session->internals.db_remove_func != NULL) + ret = + session->internals.db_remove_func (session->internals.db_ptr, + session_id); + + return (ret == 0 ? ret : GNUTLS_E_DB_ERROR); + +} + +/** + * gnutls_db_remove_session - This function will remove the current session data from the database + * @session: is a #gnutls_session_t structure. + * + * This function will remove the current session data from the session + * database. This will prevent future handshakes reusing these session + * data. This function should be called if a session was terminated + * abnormally, and before gnutls_deinit() is called. + * + * Normally gnutls_deinit() will remove abnormally terminated sessions. + * + **/ +void +gnutls_db_remove_session (gnutls_session_t session) +{ + /* if the session has failed abnormally it has + * to be removed from the db + */ + _gnutls_db_remove_session (session, + session->security_parameters.session_id, + session->security_parameters.session_id_size); +} diff --git a/src/daemon/https/tls/gnutls_db.h b/src/daemon/https/tls/gnutls_db.h new file mode 100644 index 00000000..9adece4e --- /dev/null +++ b/src/daemon/https/tls/gnutls_db.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +int _gnutls_server_register_current_session (gnutls_session_t session); +int _gnutls_server_restore_session (gnutls_session_t session, + uint8_t * session_id, + int session_id_size); +int _gnutls_db_remove_session (gnutls_session_t session, uint8_t * session_id, + int session_id_size); +int _gnutls_store_session (gnutls_session_t session, + gnutls_datum_t session_id, + gnutls_datum_t session_data); +gnutls_datum_t _gnutls_retrieve_session (gnutls_session_t session, + gnutls_datum_t session_id); +int _gnutls_remove_session (gnutls_session_t session, + gnutls_datum_t session_id); diff --git a/src/daemon/https/tls/gnutls_dh.c b/src/daemon/https/tls/gnutls_dh.c new file mode 100644 index 00000000..19322c73 --- /dev/null +++ b/src/daemon/https/tls/gnutls_dh.c @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +#include +#include + + +/* + --Example-- + you: X = g ^ x mod p; + peer:Y = g ^ y mod p; + + your_key = Y ^ x mod p; + his_key = X ^ y mod p; + +// generate our secret and the public value (X) for it + X = gnutls_calc_dh_secret(&x, g, p); +// now we can calculate the shared secret + key = gnutls_calc_dh_key(Y, x, g, p); + _gnutls_mpi_release(x); + _gnutls_mpi_release(g); +*/ + +#define MAX_BITS 18000 + +/* returns the public value (X), and the secret (ret_x). + */ +mpi_t +gnutls_calc_dh_secret (mpi_t * ret_x, mpi_t g, mpi_t prime) +{ + mpi_t e, x; + int x_size = _gnutls_mpi_get_nbits (prime) - 1; + /* The size of the secret key is less than + * prime/2 + */ + + if (x_size > MAX_BITS || x_size <= 0) + { + gnutls_assert (); + return NULL; + } + + x = _gnutls_mpi_new (x_size); + if (x == NULL) + { + gnutls_assert (); + if (ret_x) + *ret_x = NULL; + + return NULL; + } + + /* FIXME: (x_size/8)*8 is there to overcome a bug in libgcrypt + * which does not really check the bits given but the bytes. + */ + do + { + _gnutls_mpi_randomize (x, (x_size / 8) * 8, GCRY_STRONG_RANDOM); + /* Check whether x is zero. + */ + } + while (_gnutls_mpi_cmp_ui (x, 0) == 0); + + e = _gnutls_mpi_alloc_like (prime); + if (e == NULL) + { + gnutls_assert (); + if (ret_x) + *ret_x = NULL; + + _gnutls_mpi_release (&x); + return NULL; + } + + _gnutls_mpi_powm (e, g, x, prime); + + if (ret_x) + *ret_x = x; + else + _gnutls_mpi_release (&x); + return e; +} + + +mpi_t +gnutls_calc_dh_key (mpi_t f, mpi_t x, mpi_t prime) +{ + mpi_t k; + int bits; + + bits = _gnutls_mpi_get_nbits (prime); + if (bits <= 0 || bits > MAX_BITS) + { + gnutls_assert (); + return NULL; + } + + k = _gnutls_mpi_alloc_like (prime); + if (k == NULL) + return NULL; + _gnutls_mpi_powm (k, f, x, prime); + return k; +} + +/*- + * _gnutls_get_dh_params - Returns the DH parameters pointer + * @dh_params: is an DH parameters structure, or NULL. + * @func: is a callback function to receive the parameters or NULL. + * @session: a gnutls session. + * + * This function will return the dh parameters pointer. + * + -*/ +gnutls_dh_params_t +_gnutls_get_dh_params (gnutls_dh_params_t dh_params, + gnutls_params_function * func, + gnutls_session_t session) +{ + gnutls_params_st params; + int ret; + + /* if cached return the cached */ + if (session->internals.params.dh_params) + return session->internals.params.dh_params; + + if (dh_params) + { + session->internals.params.dh_params = dh_params; + } + else if (func) + { + ret = func (session, GNUTLS_PARAMS_DH, ¶ms); + if (ret == 0 && params.type == GNUTLS_PARAMS_DH) + { + session->internals.params.dh_params = params.params.dh; + session->internals.params.free_dh_params = params.deinit; + } + } + + return session->internals.params.dh_params; +} diff --git a/src/daemon/https/tls/gnutls_dh.h b/src/daemon/https/tls/gnutls_dh.h new file mode 100644 index 00000000..dc8a129d --- /dev/null +++ b/src/daemon/https/tls/gnutls_dh.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +#ifndef GNUTLS_DH_H +# define GNUTLS_DH_H + +const mpi_t *_gnutls_dh_params_to_mpi (gnutls_dh_params_t); +mpi_t gnutls_calc_dh_secret (mpi_t * ret_x, mpi_t g, mpi_t prime); +mpi_t gnutls_calc_dh_key (mpi_t f, mpi_t x, mpi_t prime); +int _gnutls_dh_generate_prime (mpi_t * ret_g, mpi_t * ret_n, unsigned bits); + +gnutls_dh_params_t +_gnutls_get_dh_params (gnutls_dh_params_t dh_params, + gnutls_params_function * func, + gnutls_session_t session); + +#endif diff --git a/src/daemon/https/tls/gnutls_dh_primes.c b/src/daemon/https/tls/gnutls_dh_primes.c new file mode 100644 index 00000000..0aea5b5f --- /dev/null +++ b/src/daemon/https/tls/gnutls_dh_primes.c @@ -0,0 +1,626 @@ +/* + * Copyright (C) 2000, 2001, 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +#include +#include +#include +#include /* for PKCS3 PEM decoding */ +#include +#include +#include "debug.h" +/* x509 */ +#include "mpi.h" + + +/* returns the prime and the generator of DH params. + */ +const mpi_t * +_gnutls_dh_params_to_mpi (gnutls_dh_params_t dh_primes) +{ + if (dh_primes == NULL || dh_primes->params[1] == NULL + || dh_primes->params[0] == NULL) + { + return NULL; + } + + return dh_primes->params; +} + +int +_gnutls_dh_generate_prime (mpi_t * ret_g, mpi_t * ret_n, unsigned int bits) +{ + mpi_t g = NULL, prime = NULL; + gcry_error_t err; + int result, times = 0, qbits; + mpi_t *factors = NULL; + + /* Calculate the size of a prime factor of (prime-1)/2. + * This is an emulation of the values in "Selecting Cryptographic Key Sizes" paper. + */ + if (bits < 256) + qbits = bits / 2; + else + { + qbits = (bits / 40) + 105; + } + + if (qbits & 1) /* better have an even number */ + qbits++; + + /* find a prime number of size bits. + */ + do + { + + if (times) + { + _gnutls_mpi_release (&prime); + gcry_prime_release_factors (factors); + } + + err = gcry_prime_generate (&prime, bits, qbits, &factors, NULL, NULL, + GCRY_STRONG_RANDOM, + GCRY_PRIME_FLAG_SPECIAL_FACTOR); + + if (err != 0) + { + gnutls_assert (); + result = GNUTLS_E_INTERNAL_ERROR; + goto cleanup; + } + + err = gcry_prime_check (prime, 0); + + times++; + } + while (err != 0 && times < 10); + + if (err != 0) + { + gnutls_assert (); + result = GNUTLS_E_INTERNAL_ERROR; + goto cleanup; + } + + /* generate the group generator. + */ + err = gcry_prime_group_generator (&g, prime, factors, NULL); + if (err != 0) + { + gnutls_assert (); + result = GNUTLS_E_INTERNAL_ERROR; + goto cleanup; + } + + gcry_prime_release_factors (factors); + factors = NULL; + + if (ret_g) + *ret_g = g; + else + _gnutls_mpi_release (&g); + if (ret_n) + *ret_n = prime; + else + _gnutls_mpi_release (&prime); + + return 0; + +cleanup:gcry_prime_release_factors (factors); + _gnutls_mpi_release (&g); + _gnutls_mpi_release (&prime); + + return result; + +} + +/* Replaces the prime in the static DH parameters, with a randomly + * generated one. + */ +/** + * gnutls_dh_params_import_raw - This function will import DH parameters + * @dh_params: Is a structure that will hold the prime numbers + * @prime: holds the new prime + * @generator: holds the new generator + * + * This function will replace the pair of prime and generator for use in + * the Diffie-Hellman key exchange. The new parameters should be stored in the + * appropriate gnutls_datum. + * + **/ +int +gnutls_dh_params_import_raw (gnutls_dh_params_t dh_params, + const gnutls_datum_t * prime, + const gnutls_datum_t * generator) +{ + mpi_t tmp_prime, tmp_g; + size_t siz; + + siz = prime->size; + if (_gnutls_mpi_scan_nz (&tmp_prime, prime->data, &siz)) + { + gnutls_assert (); + return GNUTLS_E_MPI_SCAN_FAILED; + } + + siz = generator->size; + if (_gnutls_mpi_scan_nz (&tmp_g, generator->data, &siz)) + { + _gnutls_mpi_release (&tmp_prime); + gnutls_assert (); + return GNUTLS_E_MPI_SCAN_FAILED; + } + + /* store the generated values + */ + dh_params->params[0] = tmp_prime; + dh_params->params[1] = tmp_g; + + return 0; + +} + +/** + * gnutls_dh_params_init - This function will initialize the DH parameters + * @dh_params: Is a structure that will hold the prime numbers + * + * This function will initialize the DH parameters structure. + * + **/ +int +gnutls_dh_params_init (gnutls_dh_params_t * dh_params) +{ + + (*dh_params) = gnutls_calloc (1, sizeof (dh_params_st)); + if (*dh_params == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + return 0; + +} + +/** + * gnutls_dh_params_deinit - This function will deinitialize the DH parameters + * @dh_params: Is a structure that holds the prime numbers + * + * This function will deinitialize the DH parameters structure. + * + **/ +void +gnutls_dh_params_deinit (gnutls_dh_params_t dh_params) +{ + if (dh_params == NULL) + return; + + _gnutls_mpi_release (&dh_params->params[0]); + _gnutls_mpi_release (&dh_params->params[1]); + + gnutls_free (dh_params); + +} + +/** + * gnutls_dh_params_cpy - This function will copy a DH parameters structure + * @dst: Is the destination structure, which should be initialized. + * @src: Is the source structure + * + * This function will copy the DH parameters structure from source + * to destination. + * + **/ +int +gnutls_dh_params_cpy (gnutls_dh_params_t dst, gnutls_dh_params_t src) +{ + if (src == NULL) + return GNUTLS_E_INVALID_REQUEST; + + dst->params[0] = _gnutls_mpi_copy (src->params[0]); + dst->params[1] = _gnutls_mpi_copy (src->params[1]); + + if (dst->params[0] == NULL || dst->params[1] == NULL) + return GNUTLS_E_MEMORY_ERROR; + + return 0; +} + +/** + * gnutls_dh_params_generate2 - This function will generate new DH parameters + * @params: Is the structure that the DH parameters will be stored + * @bits: is the prime's number of bits + * + * This function will generate a new pair of prime and generator for use in + * the Diffie-Hellman key exchange. The new parameters will be allocated using + * gnutls_malloc() and will be stored in the appropriate datum. + * This function is normally slow. + * + * Note that the bits value should be one of 768, 1024, 2048, 3072 or 4096. + * Also note that the DH parameters are only useful to servers. + * Since clients use the parameters sent by the server, it's of + * no use to call this in client side. + * + **/ +int +gnutls_dh_params_generate2 (gnutls_dh_params_t params, unsigned int bits) +{ + int ret; + + ret = + _gnutls_dh_generate_prime (¶ms->params[1], ¶ms->params[0], bits); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + return 0; +} + +/** + * gnutls_dh_params_import_pkcs3 - This function will import DH params from a pkcs3 structure + * @params: A structure where the parameters will be copied to + * @pkcs3_params: should contain a PKCS3 DHParams structure PEM or DER encoded + * @format: the format of params. PEM or DER. + * + * This function will extract the DHParams found in a PKCS3 formatted + * structure. This is the format generated by "openssl dhparam" tool. + * + * If the structure is PEM encoded, it should have a header + * of "BEGIN DH PARAMETERS". + * + * In case of failure a negative value will be returned, and + * 0 on success. + * + **/ +int +gnutls_dh_params_import_pkcs3 (gnutls_dh_params_t params, + const gnutls_datum_t * pkcs3_params, + gnutls_x509_crt_fmt_t format) +{ + ASN1_TYPE c2; + int result, need_free = 0; + gnutls_datum_t _params; + + if (format == GNUTLS_X509_FMT_PEM) + { + opaque *out; + + result = _gnutls_fbase64_decode ("DH PARAMETERS", pkcs3_params->data, + pkcs3_params->size, &out); + + if (result <= 0) + { + if (result == 0) + result = GNUTLS_E_INTERNAL_ERROR; + gnutls_assert (); + return result; + } + + _params.data = out; + _params.size = result; + + need_free = 1; + + } + else + { + _params.data = pkcs3_params->data; + _params.size = pkcs3_params->size; + } + + if ((result = + asn1_create_element (_gnutls_get_gnutls_asn (), "GNUTLS.DHParameter", + &c2)) != ASN1_SUCCESS) + { + gnutls_assert (); + if (need_free != 0) + { + gnutls_free (_params.data); + _params.data = NULL; + } + return _gnutls_asn2err (result); + } + + result = asn1_der_decoding (&c2, _params.data, _params.size, NULL); + + if (need_free != 0) + { + gnutls_free (_params.data); + _params.data = NULL; + } + + if (result != ASN1_SUCCESS) + { + /* couldn't decode DER */ + + _gnutls_x509_log ("DHParams: Decoding error %d\n", result); + gnutls_assert (); + asn1_delete_structure (&c2); + return _gnutls_asn2err (result); + } + + /* Read PRIME + */ + result = _gnutls_x509_read_int (c2, "prime", ¶ms->params[0]); + if (result < 0) + { + asn1_delete_structure (&c2); + gnutls_assert (); + return result; + } + + /* read the generator + */ + result = _gnutls_x509_read_int (c2, "base", ¶ms->params[1]); + if (result < 0) + { + asn1_delete_structure (&c2); + _gnutls_mpi_release (¶ms->params[0]); + gnutls_assert (); + return result; + } + + asn1_delete_structure (&c2); + + return 0; +} + +/** + * gnutls_dh_params_export_pkcs3 - This function will export DH params to a pkcs3 structure + * @params: Holds the DH parameters + * @format: the format of output params. One of PEM or DER. + * @params_data: will contain a PKCS3 DHParams structure PEM or DER encoded + * @params_data_size: holds the size of params_data (and will be replaced by the actual size of parameters) + * + * This function will export the given dh parameters to a PKCS3 + * DHParams structure. This is the format generated by "openssl dhparam" tool. + * If the buffer provided is not long enough to hold the output, then + * GNUTLS_E_SHORT_MEMORY_BUFFER will be returned. + * + * If the structure is PEM encoded, it will have a header + * of "BEGIN DH PARAMETERS". + * + * In case of failure a negative value will be returned, and + * 0 on success. + * + **/ +int +gnutls_dh_params_export_pkcs3 (gnutls_dh_params_t params, + gnutls_x509_crt_fmt_t format, + unsigned char *params_data, + size_t * params_data_size) +{ + ASN1_TYPE c2; + int result, _params_data_size; + size_t g_size, p_size; + opaque *p_data, *g_data; + opaque *all_data; + + _gnutls_mpi_print_lz (NULL, &g_size, params->params[1]); + _gnutls_mpi_print_lz (NULL, &p_size, params->params[0]); + + all_data = gnutls_malloc (g_size + p_size); + if (all_data == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + p_data = &all_data[0]; + g_data = &all_data[p_size]; + + _gnutls_mpi_print_lz (p_data, &p_size, params->params[0]); + _gnutls_mpi_print_lz (g_data, &g_size, params->params[1]); + + /* Ok. Now we have the data. Create the asn1 structures + */ + + if ((result = + asn1_create_element (_gnutls_get_gnutls_asn (), "GNUTLS.DHParameter", + &c2)) != ASN1_SUCCESS) + { + gnutls_assert (); + gnutls_free (all_data); + return _gnutls_asn2err (result); + } + + /* Write PRIME + */ + if ((result = + asn1_write_value (c2, "prime", p_data, p_size)) != ASN1_SUCCESS) + { + gnutls_assert (); + gnutls_free (all_data); + asn1_delete_structure (&c2); + return _gnutls_asn2err (result); + } + + /* Write the GENERATOR + */ + if ((result = + asn1_write_value (c2, "base", g_data, g_size)) != ASN1_SUCCESS) + { + gnutls_assert (); + gnutls_free (all_data); + asn1_delete_structure (&c2); + return _gnutls_asn2err (result); + } + + gnutls_free (all_data); + + if ((result = asn1_write_value (c2, "privateValueLength", + NULL, 0)) != ASN1_SUCCESS) + { + gnutls_assert (); + asn1_delete_structure (&c2); + return _gnutls_asn2err (result); + } + + if (format == GNUTLS_X509_FMT_DER) + { + if (params_data == NULL) + *params_data_size = 0; + + _params_data_size = *params_data_size; + result = + asn1_der_coding (c2, "", params_data, &_params_data_size, NULL); + *params_data_size = _params_data_size; + asn1_delete_structure (&c2); + + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + if (result == ASN1_MEM_ERROR) + return GNUTLS_E_SHORT_MEMORY_BUFFER; + + return _gnutls_asn2err (result); + } + + } + else + { /* PEM */ + opaque *tmp; + opaque *out; + int len; + + len = 0; + asn1_der_coding (c2, "", NULL, &len, NULL); + + tmp = gnutls_malloc (len); + if (tmp == NULL) + { + gnutls_assert (); + asn1_delete_structure (&c2); + return GNUTLS_E_MEMORY_ERROR; + } + + if ((result = + asn1_der_coding (c2, "", tmp, &len, NULL)) != ASN1_SUCCESS) + { + gnutls_assert (); + gnutls_free (tmp); + asn1_delete_structure (&c2); + return _gnutls_asn2err (result); + } + + asn1_delete_structure (&c2); + + result = _gnutls_fbase64_encode ("DH PARAMETERS", tmp, len, &out); + + gnutls_free (tmp); + + if (result < 0) + { + gnutls_assert (); + return result; + } + + if (result == 0) + { /* oooops */ + gnutls_assert (); + gnutls_free (out); + return GNUTLS_E_INTERNAL_ERROR; + } + + if ((unsigned) result + 1 > *params_data_size) + { + gnutls_assert (); + gnutls_free (out); + *params_data_size = result + 1; + return GNUTLS_E_SHORT_MEMORY_BUFFER; + } + + *params_data_size = result; + + if (params_data) + { + memcpy (params_data, out, result); + params_data[result] = 0; + } + gnutls_free (out); + + } + + return 0; +} + +/** + * gnutls_dh_params_export_raw - This function will export the raw DH parameters + * @params: Holds the DH parameters + * @prime: will hold the new prime + * @generator: will hold the new generator + * @bits: if non null will hold is the prime's number of bits + * + * This function will export the pair of prime and generator for use in + * the Diffie-Hellman key exchange. The new parameters will be allocated using + * gnutls_malloc() and will be stored in the appropriate datum. + * + **/ +int +gnutls_dh_params_export_raw (gnutls_dh_params_t params, + gnutls_datum_t * prime, + gnutls_datum_t * generator, unsigned int *bits) +{ + + size_t size; + + if (params->params[1] == NULL || params->params[0] == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + size = 0; + _gnutls_mpi_print (NULL, &size, params->params[1]); + + generator->data = gnutls_malloc (size); + if (generator->data == NULL) + { + return GNUTLS_E_MEMORY_ERROR; + } + + generator->size = size; + _gnutls_mpi_print (generator->data, &size, params->params[1]); + + size = 0; + _gnutls_mpi_print (NULL, &size, params->params[0]); + + prime->data = gnutls_malloc (size); + if (prime->data == NULL) + { + gnutls_free (generator->data); + generator->data = NULL; + return GNUTLS_E_MEMORY_ERROR; + } + prime->size = size; + _gnutls_mpi_print (prime->data, &size, params->params[0]); + + if (bits) + *bits = _gnutls_mpi_get_nbits (params->params[0]); + + return 0; + +} diff --git a/src/daemon/https/tls/gnutls_errors.c b/src/daemon/https/tls/gnutls_errors.c new file mode 100644 index 00000000..9916dc96 --- /dev/null +++ b/src/daemon/https/tls/gnutls_errors.c @@ -0,0 +1,422 @@ +/* + * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +#include +#include "gnutls_errors.h" +#include +#ifdef STDC_HEADERS +# include +#endif + +/* I18n of error codes. */ +#include "gettext.h" +#define _(String) dgettext (PACKAGE, String) +#define N_(String) gettext_noop (String) + +extern LOG_FUNC _gnutls_log_func; + +#define ERROR_ENTRY(desc, name, fatal) \ + { desc, #name, name, fatal} + +struct gnutls_error_entry +{ + const char *desc; + const char *_name; + int number; + int fatal; +}; +typedef struct gnutls_error_entry gnutls_error_entry; + +static const gnutls_error_entry error_algorithms[] = { + /* "Short Description", Error code define, critical (0,1) -- 1 in most cases */ + ERROR_ENTRY (N_("Success."), GNUTLS_E_SUCCESS, 0), + ERROR_ENTRY (N_("Could not negotiate a supported cipher suite."), + GNUTLS_E_UNKNOWN_CIPHER_SUITE, 1), + ERROR_ENTRY (N_("The cipher type is unsupported."), + GNUTLS_E_UNKNOWN_CIPHER_TYPE, 1), + ERROR_ENTRY (N_("The certificate and the given key do not match."), + GNUTLS_E_CERTIFICATE_KEY_MISMATCH, 1), + ERROR_ENTRY (N_("Could not negotiate a supported compression method."), + GNUTLS_E_UNKNOWN_COMPRESSION_ALGORITHM, 1), + ERROR_ENTRY (N_("An unknown public key algorithm was encountered."), + GNUTLS_E_UNKNOWN_PK_ALGORITHM, 1), + + ERROR_ENTRY (N_("An algorithm that is not enabled was negotiated."), + GNUTLS_E_UNWANTED_ALGORITHM, 1), + ERROR_ENTRY (N_("A large TLS record packet was received."), + GNUTLS_E_LARGE_PACKET, 1), + ERROR_ENTRY (N_("A record packet with illegal version was received."), + GNUTLS_E_UNSUPPORTED_VERSION_PACKET, 1), + ERROR_ENTRY (N_ + ("The Diffie Hellman prime sent by the server is not acceptable (not long enough)."), + GNUTLS_E_DH_PRIME_UNACCEPTABLE, 1), + ERROR_ENTRY (N_("A TLS packet with unexpected length was received."), + GNUTLS_E_UNEXPECTED_PACKET_LENGTH, 1), + ERROR_ENTRY (N_ + ("The specified session has been invalidated for some reason."), + GNUTLS_E_INVALID_SESSION, 1), + + ERROR_ENTRY (N_("GnuTLS internal error."), GNUTLS_E_INTERNAL_ERROR, 1), + ERROR_ENTRY (N_("An illegal TLS extension was received."), + GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION, 1), + ERROR_ENTRY (N_("A TLS fatal alert has been received."), + GNUTLS_E_FATAL_ALERT_RECEIVED, 1), + ERROR_ENTRY (N_("An unexpected TLS packet was received."), + GNUTLS_E_UNEXPECTED_PACKET, 1), + ERROR_ENTRY (N_("A TLS warning alert has been received."), + GNUTLS_E_WARNING_ALERT_RECEIVED, 0), + ERROR_ENTRY (N_ + ("An error was encountered at the TLS Finished packet calculation."), + GNUTLS_E_ERROR_IN_FINISHED_PACKET, 1), + ERROR_ENTRY (N_("The peer did not send any certificate."), + GNUTLS_E_NO_CERTIFICATE_FOUND, 1), + + ERROR_ENTRY (N_("No temporary RSA parameters were found."), + GNUTLS_E_NO_TEMPORARY_RSA_PARAMS, 1), + ERROR_ENTRY (N_("No temporary DH parameters were found."), + GNUTLS_E_NO_TEMPORARY_DH_PARAMS, 1), + ERROR_ENTRY (N_("An unexpected TLS handshake packet was received."), + GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET, 1), + ERROR_ENTRY (N_("The scanning of a large integer has failed."), + GNUTLS_E_MPI_SCAN_FAILED, 1), + ERROR_ENTRY (N_("Could not export a large integer."), + GNUTLS_E_MPI_PRINT_FAILED, 1), + ERROR_ENTRY (N_("Decryption has failed."), GNUTLS_E_DECRYPTION_FAILED, 1), + ERROR_ENTRY (N_("Encryption has failed."), GNUTLS_E_ENCRYPTION_FAILED, 1), + ERROR_ENTRY (N_("Public key decryption has failed."), + GNUTLS_E_PK_DECRYPTION_FAILED, 1), + ERROR_ENTRY (N_("Public key encryption has failed."), + GNUTLS_E_PK_ENCRYPTION_FAILED, 1), + ERROR_ENTRY (N_("Public key signing has failed."), GNUTLS_E_PK_SIGN_FAILED, + 1), + ERROR_ENTRY (N_("Public key signature verification has failed."), + GNUTLS_E_PK_SIG_VERIFY_FAILED, 1), + ERROR_ENTRY (N_("Decompression of the TLS record packet has failed."), + GNUTLS_E_DECOMPRESSION_FAILED, 1), + ERROR_ENTRY (N_("Compression of the TLS record packet has failed."), + GNUTLS_E_COMPRESSION_FAILED, 1), + + ERROR_ENTRY (N_("Internal error in memory allocation."), + GNUTLS_E_MEMORY_ERROR, 1), + ERROR_ENTRY (N_("An unimplemented or disabled feature has been requested."), + GNUTLS_E_UNIMPLEMENTED_FEATURE, 1), + ERROR_ENTRY (N_("Insufficient credentials for that request."), + GNUTLS_E_INSUFFICIENT_CREDENTIALS, 1), + ERROR_ENTRY (N_("Error in password file."), GNUTLS_E_SRP_PWD_ERROR, 1), + ERROR_ENTRY (N_("Wrong padding in PKCS1 packet."), GNUTLS_E_PKCS1_WRONG_PAD, + 1), + ERROR_ENTRY (N_("The requested session has expired."), GNUTLS_E_EXPIRED, 1), + ERROR_ENTRY (N_("Hashing has failed."), GNUTLS_E_HASH_FAILED, 1), + ERROR_ENTRY (N_("Base64 decoding error."), GNUTLS_E_BASE64_DECODING_ERROR, + 1), + ERROR_ENTRY (N_("Base64 unexpected header error."), + GNUTLS_E_BASE64_UNEXPECTED_HEADER_ERROR, + 1), + ERROR_ENTRY (N_("Base64 encoding error."), GNUTLS_E_BASE64_ENCODING_ERROR, + 1), + ERROR_ENTRY (N_("Parsing error in password file."), + GNUTLS_E_SRP_PWD_PARSING_ERROR, 1), + ERROR_ENTRY (N_("The requested data were not available."), + GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE, 0), + ERROR_ENTRY (N_("Error in the pull function."), GNUTLS_E_PULL_ERROR, 1), + ERROR_ENTRY (N_("Error in the push function."), GNUTLS_E_PUSH_ERROR, 1), + ERROR_ENTRY (N_ + ("The upper limit of record packet sequence numbers has been reached. Wow!"), + GNUTLS_E_RECORD_LIMIT_REACHED, 1), + ERROR_ENTRY (N_("Error in the certificate."), GNUTLS_E_CERTIFICATE_ERROR, + 1), + ERROR_ENTRY (N_("Unknown Subject Alternative name in X.509 certificate."), + GNUTLS_E_X509_UNKNOWN_SAN, 1), + + ERROR_ENTRY (N_("Unsupported critical extension in X.509 certificate."), + GNUTLS_E_X509_UNSUPPORTED_CRITICAL_EXTENSION, 1), + ERROR_ENTRY (N_("Key usage violation in certificate has been detected."), + GNUTLS_E_KEY_USAGE_VIOLATION, 1), + ERROR_ENTRY (N_("Function was interrupted."), GNUTLS_E_AGAIN, 0), + ERROR_ENTRY (N_("Function was interrupted."), GNUTLS_E_INTERRUPTED, 0), + ERROR_ENTRY (N_("Rehandshake was requested by the peer."), + GNUTLS_E_REHANDSHAKE, 0), + ERROR_ENTRY (N_ + ("TLS Application data were received, while expecting handshake data."), + GNUTLS_E_GOT_APPLICATION_DATA, 1), + ERROR_ENTRY (N_("Error in Database backend."), GNUTLS_E_DB_ERROR, 1), + ERROR_ENTRY (N_("The certificate type is not supported."), + GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE, 1), + ERROR_ENTRY (N_("The given memory buffer is too short to hold parameters."), + GNUTLS_E_SHORT_MEMORY_BUFFER, 1), + ERROR_ENTRY (N_("The request is invalid."), GNUTLS_E_INVALID_REQUEST, 1), + ERROR_ENTRY (N_("An illegal parameter has been received."), + GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER, 1), + ERROR_ENTRY (N_("Error while reading file."), GNUTLS_E_FILE_ERROR, 1), + + ERROR_ENTRY (N_("ASN1 parser: Element was not found."), + GNUTLS_E_ASN1_ELEMENT_NOT_FOUND, 1), + ERROR_ENTRY (N_("ASN1 parser: Identifier was not found"), + GNUTLS_E_ASN1_IDENTIFIER_NOT_FOUND, 1), + ERROR_ENTRY (N_("ASN1 parser: Error in DER parsing."), + GNUTLS_E_ASN1_DER_ERROR, 1), + ERROR_ENTRY (N_("ASN1 parser: Value was not found."), + GNUTLS_E_ASN1_VALUE_NOT_FOUND, 1), + ERROR_ENTRY (N_("ASN1 parser: Generic parsing error."), + GNUTLS_E_ASN1_GENERIC_ERROR, 1), + ERROR_ENTRY (N_("ASN1 parser: Value is not valid."), + GNUTLS_E_ASN1_VALUE_NOT_VALID, 1), + ERROR_ENTRY (N_("ASN1 parser: Error in TAG."), GNUTLS_E_ASN1_TAG_ERROR, 1), + ERROR_ENTRY (N_("ASN1 parser: error in implicit tag"), + GNUTLS_E_ASN1_TAG_IMPLICIT, 1), + ERROR_ENTRY (N_("ASN1 parser: Error in type 'ANY'."), + GNUTLS_E_ASN1_TYPE_ANY_ERROR, 1), + ERROR_ENTRY (N_("ASN1 parser: Syntax error."), GNUTLS_E_ASN1_SYNTAX_ERROR, + 1), + ERROR_ENTRY (N_("ASN1 parser: Overflow in DER parsing."), + GNUTLS_E_ASN1_DER_OVERFLOW, 1), + + ERROR_ENTRY (N_("Too many empty record packets have been received."), + GNUTLS_E_TOO_MANY_EMPTY_PACKETS, 1), + ERROR_ENTRY (N_("The initialization of GnuTLS-extra has failed."), + GNUTLS_E_INIT_LIBEXTRA, 1), + ERROR_ENTRY (N_ + ("The GnuTLS library version does not match the GnuTLS-extra library version."), + GNUTLS_E_LIBRARY_VERSION_MISMATCH, 1), + ERROR_ENTRY (N_("The gcrypt library version is too old."), + GNUTLS_E_INCOMPATIBLE_GCRYPT_LIBRARY, 1), + + ERROR_ENTRY (N_("The tasn1 library version is too old."), + GNUTLS_E_INCOMPATIBLE_LIBTASN1_LIBRARY, 1), + + ERROR_ENTRY (N_("Error loading the keyring."), + GNUTLS_E_OPENPGP_KEYRING_ERROR, 1), + ERROR_ENTRY (N_("The initialization of LZO has failed."), + GNUTLS_E_LZO_INIT_FAILED, 1), + ERROR_ENTRY (N_("No supported compression algorithms have been found."), + GNUTLS_E_NO_COMPRESSION_ALGORITHMS, 1), + ERROR_ENTRY (N_("No supported cipher suites have been found."), + GNUTLS_E_NO_CIPHER_SUITES, 1), + ERROR_ENTRY (N_("Could not get OpenPGP key."), + GNUTLS_E_OPENPGP_GETKEY_FAILED, 1), + + ERROR_ENTRY (N_("The SRP username supplied is illegal."), + GNUTLS_E_ILLEGAL_SRP_USERNAME, 1), + + ERROR_ENTRY (N_("The OpenPGP fingerprint is not supported."), + GNUTLS_E_OPENPGP_FINGERPRINT_UNSUPPORTED, 1), + ERROR_ENTRY (N_("The certificate has unsupported attributes."), + GNUTLS_E_X509_UNSUPPORTED_ATTRIBUTE, 1), + ERROR_ENTRY (N_("The OID is not supported."), GNUTLS_E_X509_UNSUPPORTED_OID, + 1), + ERROR_ENTRY (N_("The hash algorithm is unknown."), + GNUTLS_E_UNKNOWN_HASH_ALGORITHM, 1), + ERROR_ENTRY (N_("The PKCS structure's content type is unknown."), + GNUTLS_E_UNKNOWN_PKCS_CONTENT_TYPE, 1), + ERROR_ENTRY (N_("The PKCS structure's bag type is unknown."), + GNUTLS_E_UNKNOWN_PKCS_BAG_TYPE, 1), + ERROR_ENTRY (N_("The given password contains invalid characters."), + GNUTLS_E_INVALID_PASSWORD, 1), + ERROR_ENTRY (N_("The Message Authentication Code verification failed."), + GNUTLS_E_MAC_VERIFY_FAILED, 1), + ERROR_ENTRY (N_("Some constraint limits were reached."), + GNUTLS_E_CONSTRAINT_ERROR, 1), + ERROR_ENTRY (N_("Failed to acquire random data."), GNUTLS_E_RANDOM_FAILED, + 1), + + ERROR_ENTRY (N_("Received a TLS/IA Intermediate Phase Finished message"), + GNUTLS_E_WARNING_IA_IPHF_RECEIVED, 0), + ERROR_ENTRY (N_("Received a TLS/IA Final Phase Finished message"), + GNUTLS_E_WARNING_IA_FPHF_RECEIVED, 0), + ERROR_ENTRY (N_("Verifying TLS/IA phase checksum failed"), + GNUTLS_E_IA_VERIFY_FAILED, 1), + + ERROR_ENTRY (N_("The specified algorithm or protocol is unknown."), + GNUTLS_E_UNKNOWN_ALGORITHM, 1), + + {NULL, NULL, 0, 0} +}; + +#define GNUTLS_ERROR_LOOP(b) \ + const gnutls_error_entry *p; \ + for(p = error_algorithms; p->desc != NULL; p++) { b ; } + +#define GNUTLS_ERROR_ALG_LOOP(a) \ + GNUTLS_ERROR_LOOP( if(p->number == error) { a; break; } ) + + + +/** + * gnutls_error_is_fatal - Returns non-zero in case of a fatal error + * @error: is an error returned by a gnutls function. Error should be a negative value. + * + * If a function returns a negative value you may feed that value + * to this function to see if it is fatal. Returns 1 for a fatal + * error 0 otherwise. However you may want to check the + * error code manually, since some non-fatal errors to the protocol + * may be fatal for you (your program). + * + * This is only useful if you are dealing with errors from the + * record layer or the handshake layer. + * + * For positive @error values, 0 is returned. + * + **/ +int +gnutls_error_is_fatal (int error) +{ + int ret = 1; + + /* Input sanitzation. Positive values are not errors at all, and + definitely not fatal. */ + if (error > 0) + return 0; + + GNUTLS_ERROR_ALG_LOOP (ret = p->fatal); + + return ret; +} + +/** + * gnutls_perror - prints a string to stderr with a description of an error + * @error: is an error returned by a gnutls function. Error is always a negative value. + * + * This function is like perror(). The only difference is that it accepts an + * error number returned by a gnutls function. + **/ +void +gnutls_perror (int error) +{ + const char *ret = NULL; + + /* avoid prefix */ + GNUTLS_ERROR_ALG_LOOP (ret = p->desc); + if (ret == NULL) + ret = "(unknown)"; + fprintf (stderr, "GNUTLS ERROR: %s\n", _(ret)); +} + + +/** + * gnutls_strerror - Returns a string with a description of an error + * @error: is an error returned by a gnutls function. Error is always a negative value. + * + * This function is similar to strerror(). Differences: it accepts an error + * number returned by a gnutls function; In case of an unknown error + * a descriptive string is sent instead of NULL. + **/ +const char * +gnutls_strerror (int error) +{ + const char *ret = NULL; + + /* avoid prefix */ + GNUTLS_ERROR_ALG_LOOP (ret = p->desc); + if (ret == NULL) + return "(unknown error code)"; + return _(ret); +} + +/* This will print the actual define of the + * given error code. + */ +const char * +_gnutls_strerror (int error) +{ + const char *ret = NULL; + + /* avoid prefix */ + GNUTLS_ERROR_ALG_LOOP (ret = p->_name); + + return _(ret); +} + +int +_gnutls_asn2err (int asn_err) +{ + switch (asn_err) + { + case ASN1_FILE_NOT_FOUND: + return GNUTLS_E_FILE_ERROR; + case ASN1_ELEMENT_NOT_FOUND: + return GNUTLS_E_ASN1_ELEMENT_NOT_FOUND; + case ASN1_IDENTIFIER_NOT_FOUND: + return GNUTLS_E_ASN1_IDENTIFIER_NOT_FOUND; + case ASN1_DER_ERROR: + return GNUTLS_E_ASN1_DER_ERROR; + case ASN1_VALUE_NOT_FOUND: + return GNUTLS_E_ASN1_VALUE_NOT_FOUND; + case ASN1_GENERIC_ERROR: + return GNUTLS_E_ASN1_GENERIC_ERROR; + case ASN1_VALUE_NOT_VALID: + return GNUTLS_E_ASN1_VALUE_NOT_VALID; + case ASN1_TAG_ERROR: + return GNUTLS_E_ASN1_TAG_ERROR; + case ASN1_TAG_IMPLICIT: + return GNUTLS_E_ASN1_TAG_IMPLICIT; + case ASN1_ERROR_TYPE_ANY: + return GNUTLS_E_ASN1_TYPE_ANY_ERROR; + case ASN1_SYNTAX_ERROR: + return GNUTLS_E_ASN1_SYNTAX_ERROR; + case ASN1_MEM_ERROR: + return GNUTLS_E_SHORT_MEMORY_BUFFER; + case ASN1_MEM_ALLOC_ERROR: + return GNUTLS_E_MEMORY_ERROR; + case ASN1_DER_OVERFLOW: + return GNUTLS_E_ASN1_DER_OVERFLOW; + default: + return GNUTLS_E_ASN1_GENERIC_ERROR; + } +} + + +/* this function will output a message using the + * caller provided function + */ +void +_gnutls_log (int level, const char *fmt, ...) +{ + va_list args; + char str[MAX_LOG_SIZE]; + void (*log_func) (int, const char *) = _gnutls_log_func; + + if (_gnutls_log_func == NULL) + return; + + va_start (args, fmt); + vsnprintf (str, MAX_LOG_SIZE - 1, fmt, args); /* Flawfinder: ignore */ + va_end (args); + + log_func (level, str); +} + +#ifndef DEBUG +# ifndef C99_MACROS + +/* Without C99 macros these functions have to + * be called. This may affect performance. + */ +void +_gnutls_null_log (void *x, ...) +{ + return; +} + +# endif /* C99_MACROS */ +#endif /* DEBUG */ diff --git a/src/daemon/https/tls/gnutls_errors.h b/src/daemon/https/tls/gnutls_errors.h new file mode 100644 index 00000000..667bd62b --- /dev/null +++ b/src/daemon/https/tls/gnutls_errors.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +#include + +#define GNUTLS_E_INT_RET_0 -1251 + +#ifdef __FILE__ +# ifdef __LINE__ +# define gnutls_assert() _gnutls_debug_log( "ASSERT: %s:%d\n", __FILE__,__LINE__); +# else +# define gnutls_assert() +# endif +#else /* __FILE__ not defined */ +# define gnutls_assert() +#endif + +int _gnutls_asn2err (int asn_err); +void _gnutls_log (int, const char *fmt, ...); + +extern int _gnutls_log_level; + +#ifdef C99_MACROS +#define LEVEL(l, ...) if (_gnutls_log_level >= l || _gnutls_log_level > 9) \ + _gnutls_log( l, __VA_ARGS__) + +#define LEVEL_EQ(l, ...) if (_gnutls_log_level == l || _gnutls_log_level > 9) \ + _gnutls_log( l, __VA_ARGS__) + +# define _gnutls_debug_log(...) LEVEL(2, __VA_ARGS__) +# define _gnutls_handshake_log(...) LEVEL(3, __VA_ARGS__) +# define _gnutls_io_log(...) LEVEL_EQ(5, __VA_ARGS__) +# define _gnutls_buffers_log(...) LEVEL_EQ(6, __VA_ARGS__) +# define _gnutls_hard_log(...) LEVEL(9, __VA_ARGS__) +# define _gnutls_record_log(...) LEVEL(4, __VA_ARGS__) +# define _gnutls_read_log(...) LEVEL_EQ(7, __VA_ARGS__) +# define _gnutls_write_log(...) LEVEL_EQ(7, __VA_ARGS__) +# define _gnutls_x509_log(...) LEVEL(1, __VA_ARGS__) +#else +# define _gnutls_debug_log _gnutls_null_log +# define _gnutls_handshake_log _gnutls_null_log +# define _gnutls_io_log _gnutls_null_log +# define _gnutls_buffers_log _gnutls_null_log +# define _gnutls_hard_log _gnutls_null_log +# define _gnutls_record_log _gnutls_null_log +# define _gnutls_read_log _gnutls_null_log +# define _gnutls_write_log _gnutls_null_log +# define _gnutls_x509_log _gnutls_null_log + +void _gnutls_null_log (void *, ...); + +#endif /* C99_MACROS */ diff --git a/src/daemon/https/tls/gnutls_extensions.c b/src/daemon/https/tls/gnutls_extensions.c new file mode 100644 index 00000000..603446d7 --- /dev/null +++ b/src/daemon/https/tls/gnutls_extensions.c @@ -0,0 +1,318 @@ +/* + * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +/* Functions that relate to the TLS hello extension parsing. + * Hello extensions are packets appended in the TLS hello packet, and + * allow for extra functionality. + */ + +#include "gnutls_int.h" +#include "gnutls_extensions.h" +#include "gnutls_errors.h" +#include "ext_max_record.h" +#include +#include +#include +#include +#include + +/* Key Exchange Section */ +#define GNUTLS_EXTENSION_ENTRY(type, parse_type, ext_func_recv, ext_func_send) \ + { #type, type, parse_type, ext_func_recv, ext_func_send } + + +#define MAX_EXT_SIZE 10 +const int _gnutls_extensions_size = MAX_EXT_SIZE; + +gnutls_extension_entry _gnutls_extensions[MAX_EXT_SIZE] = { + GNUTLS_EXTENSION_ENTRY (GNUTLS_EXTENSION_MAX_RECORD_SIZE, + EXTENSION_TLS, + _gnutls_max_record_recv_params, + _gnutls_max_record_send_params), + GNUTLS_EXTENSION_ENTRY (GNUTLS_EXTENSION_CERT_TYPE, + EXTENSION_TLS, + _gnutls_cert_type_recv_params, + _gnutls_cert_type_send_params), + GNUTLS_EXTENSION_ENTRY (GNUTLS_EXTENSION_SERVER_NAME, + EXTENSION_APPLICATION, + _gnutls_server_name_recv_params, + _gnutls_server_name_send_params), +#ifdef ENABLE_OPRFI + GNUTLS_EXTENSION_ENTRY (GNUTLS_EXTENSION_OPAQUE_PRF_INPUT, + EXTENSION_TLS, + _gnutls_oprfi_recv_params, + _gnutls_oprfi_send_params), +#endif +#ifdef ENABLE_SRP + GNUTLS_EXTENSION_ENTRY (GNUTLS_EXTENSION_SRP, + EXTENSION_TLS, + _gnutls_srp_recv_params, + _gnutls_srp_send_params), +#endif + GNUTLS_EXTENSION_ENTRY (GNUTLS_EXTENSION_INNER_APPLICATION, + EXTENSION_TLS, + _gnutls_inner_application_recv_params, + _gnutls_inner_application_send_params), + {0, 0, 0, 0} +}; + +#define GNUTLS_EXTENSION_LOOP2(b) \ + gnutls_extension_entry *p; \ + for(p = _gnutls_extensions; p->name != NULL; p++) { b ; } + +#define GNUTLS_EXTENSION_LOOP(a) \ + GNUTLS_EXTENSION_LOOP2( if(p->type == type) { a; break; } ) + + +/* EXTENSION functions */ + +ext_recv_func +_gnutls_ext_func_recv (uint16_t type, tls_ext_parse_type_t parse_type) +{ + ext_recv_func ret = NULL; + GNUTLS_EXTENSION_LOOP (if + (parse_type == EXTENSION_ANY + || p->parse_type == parse_type) ret = + p->gnutls_ext_func_recv); + return ret; + +} + +ext_send_func +_gnutls_ext_func_send (uint16_t type) +{ + ext_send_func ret = NULL; + GNUTLS_EXTENSION_LOOP (ret = p->gnutls_ext_func_send); + return ret; + +} + +const char * +_gnutls_extension_get_name (uint16_t type) +{ + const char *ret = NULL; + + /* avoid prefix */ + GNUTLS_EXTENSION_LOOP (ret = p->name + sizeof ("GNUTLS_EXTENSION_") - 1); + + return ret; +} + +/* Checks if the extension we just received is one of the + * requested ones. Otherwise it's a fatal error. + */ +static int +_gnutls_extension_list_check (gnutls_session_t session, uint16_t type) +{ + if (session->security_parameters.entity == GNUTLS_CLIENT) + { + int i; + for (i = 0; i < session->internals.extensions_sent_size; i++) + { + if (type == session->internals.extensions_sent[i]) + return 0; /* ok found */ + } + return GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION; + } + + return 0; +} + +int +_gnutls_parse_extensions (gnutls_session_t session, + tls_ext_parse_type_t parse_type, + const opaque * data, int data_size) +{ + int next, ret; + int pos = 0; + uint16_t type; + const opaque *sdata; + ext_recv_func ext_recv; + uint16_t size; + +#ifdef DEBUG + int i; + + if (session->security_parameters.entity == GNUTLS_CLIENT) + for (i = 0; i < session->internals.extensions_sent_size; i++) + { + _gnutls_debug_log ("EXT[%d]: expecting extension '%s'\n", + session, + _gnutls_extension_get_name (session-> + internals. + extensions_sent[i])); + } +#endif + + DECR_LENGTH_RET (data_size, 2, 0); + next = _gnutls_read_uint16 (data); + pos += 2; + + DECR_LENGTH_RET (data_size, next, 0); + + do + { + DECR_LENGTH_RET (next, 2, 0); + type = _gnutls_read_uint16 (&data[pos]); + pos += 2; + + _gnutls_debug_log ("EXT[%x]: Received extension '%s/%d'\n", session, + _gnutls_extension_get_name (type), type); + + if ((ret = _gnutls_extension_list_check (session, type)) < 0) + { + gnutls_assert (); + return ret; + } + + DECR_LENGTH_RET (next, 2, 0); + size = _gnutls_read_uint16 (&data[pos]); + pos += 2; + + DECR_LENGTH_RET (next, size, 0); + sdata = &data[pos]; + pos += size; + + ext_recv = _gnutls_ext_func_recv (type, parse_type); + if (ext_recv == NULL) + continue; + if ((ret = ext_recv (session, sdata, size)) < 0) + { + gnutls_assert (); + return ret; + } + + } + while (next > 2); + + return 0; + +} + +/* Adds the extension we want to send in the extensions list. + * This list is used to check whether the (later) received + * extensions are the ones we requested. + */ +static void +_gnutls_extension_list_add (gnutls_session_t session, uint16_t type) +{ + + if (session->security_parameters.entity == GNUTLS_CLIENT) + { + if (session->internals.extensions_sent_size < MAX_EXT_TYPES) + { + session->internals.extensions_sent[session->internals. + extensions_sent_size] = type; + session->internals.extensions_sent_size++; + } + else + { + _gnutls_debug_log ("extensions: Increase MAX_EXT_TYPES\n"); + } + } +} + +int +_gnutls_gen_extensions (gnutls_session_t session, opaque * data, + size_t data_size) +{ + int size; + uint16_t pos = 0; + opaque *sdata; + int sdata_size; + ext_send_func ext_send; + gnutls_extension_entry *p; + + if (data_size < 2) + { + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + /* allocate enough data for each extension. + */ + sdata_size = data_size; + sdata = gnutls_malloc (sdata_size); + if (sdata == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + pos += 2; + for (p = _gnutls_extensions; p->name != NULL; p++) + { + ext_send = _gnutls_ext_func_send (p->type); + if (ext_send == NULL) + continue; + size = ext_send (session, sdata, sdata_size); + if (size > 0) + { + if (data_size < pos + (size_t) size + 4) + { + gnutls_assert (); + gnutls_free (sdata); + return GNUTLS_E_INTERNAL_ERROR; + } + + /* write extension type */ + _gnutls_write_uint16 (p->type, &data[pos]); + pos += 2; + + /* write size */ + _gnutls_write_uint16 (size, &data[pos]); + pos += 2; + + memcpy (&data[pos], sdata, size); + pos += size; + + /* add this extension to the extension list + */ + _gnutls_extension_list_add (session, p->type); + + _gnutls_debug_log ("EXT[%x]: Sending extension %s\n", session, + _gnutls_extension_get_name (p->type)); + } + else if (size < 0) + { + gnutls_assert (); + gnutls_free (sdata); + return size; + } + } + + size = pos; + pos -= 2; /* remove the size of the size header! */ + + _gnutls_write_uint16 (pos, data); + + if (size == 2) + { /* empty */ + size = 0; + } + + gnutls_free (sdata); + return size; + +} diff --git a/src/daemon/https/tls/gnutls_extensions.h b/src/daemon/https/tls/gnutls_extensions.h new file mode 100644 index 00000000..c305846c --- /dev/null +++ b/src/daemon/https/tls/gnutls_extensions.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2007 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +#include + +const char *_gnutls_extension_get_name (uint16_t type); +int _gnutls_parse_extensions (gnutls_session_t, tls_ext_parse_type_t, const opaque *, int); +int _gnutls_gen_extensions (gnutls_session_t session, opaque * data, + size_t data_size); + +typedef int (*ext_recv_func) (gnutls_session_t, const opaque *, size_t); /* recv data */ +typedef int (*ext_send_func) (gnutls_session_t, opaque *, size_t); /* send data */ + +ext_send_func _gnutls_ext_func_send (uint16_t type); +ext_recv_func _gnutls_ext_func_recv (uint16_t type, tls_ext_parse_type_t); + +typedef struct +{ + const char *name; + uint16_t type; + tls_ext_parse_type_t parse_type; + ext_recv_func gnutls_ext_func_recv; + ext_send_func gnutls_ext_func_send; +} gnutls_extension_entry; diff --git a/src/daemon/https/tls/gnutls_extra_hooks.c b/src/daemon/https/tls/gnutls_extra_hooks.c new file mode 100644 index 00000000..f7852f64 --- /dev/null +++ b/src/daemon/https/tls/gnutls_extra_hooks.c @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2007 Free Software Foundation + * + * Author: Simon Josefsson + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +#include +#include + +/* Variables used by libgnutls, set by + _gnutls_add_openpgp_functions(), typically invoked by + libgnutls_extra. */ +_gnutls_openpgp_verify_key_func _E_gnutls_openpgp_verify_key = NULL; +_gnutls_openpgp_crt_creation_time_func + _E_gnutls_openpgp_get_raw_key_creation_time = NULL; +_gnutls_openpgp_crt_expiration_time_func + _E_gnutls_openpgp_get_raw_key_expiration_time = NULL; +_gnutls_openpgp_fingerprint_func _E_gnutls_openpgp_fingerprint = NULL; +_gnutls_openpgp_crt_request_func _E_gnutls_openpgp_request_key = NULL; +_gnutls_openpgp_raw_key_to_gcert_func _E_gnutls_openpgp_raw_key_to_gcert = + NULL; +_gnutls_openpgp_raw_privkey_to_gkey_func _E_gnutls_openpgp_raw_privkey_to_gkey + = NULL; +_gnutls_openpgp_crt_to_gcert_func _E_gnutls_openpgp_crt_to_gcert = NULL; +_gnutls_openpgp_privkey_to_gkey_func _E_gnutls_openpgp_privkey_to_gkey = NULL; +_gnutls_openpgp_crt_deinit_func _E_gnutls_openpgp_crt_deinit = NULL; +_gnutls_openpgp_keyring_deinit_func _E_gnutls_openpgp_keyring_deinit = NULL; +_gnutls_openpgp_privkey_deinit_func _E_gnutls_openpgp_privkey_deinit = NULL; + +/* Called by libgnutls_extra to set the OpenPGP functions that are + needed by GnuTLS. */ +extern void + _gnutls_add_openpgp_functions + (_gnutls_openpgp_verify_key_func verify_key, + _gnutls_openpgp_crt_creation_time_func key_creation_time, + _gnutls_openpgp_crt_expiration_time_func key_expiration_time, + _gnutls_openpgp_fingerprint_func fingerprint, + _gnutls_openpgp_crt_request_func request_key, + _gnutls_openpgp_raw_key_to_gcert_func raw_key_to_gcert, + _gnutls_openpgp_raw_privkey_to_gkey_func raw_privkey_to_gkey, + _gnutls_openpgp_crt_to_gcert_func key_to_gcert, + _gnutls_openpgp_privkey_to_gkey_func privkey_to_gkey, + _gnutls_openpgp_crt_deinit_func key_deinit, + _gnutls_openpgp_keyring_deinit_func keyring_deinit, + _gnutls_openpgp_privkey_deinit_func privkey_deinit) +{ + _E_gnutls_openpgp_verify_key = verify_key; + _E_gnutls_openpgp_get_raw_key_creation_time = key_creation_time; + _E_gnutls_openpgp_get_raw_key_expiration_time = key_expiration_time; + _E_gnutls_openpgp_fingerprint = fingerprint; + _E_gnutls_openpgp_request_key = request_key; + _E_gnutls_openpgp_raw_key_to_gcert = raw_key_to_gcert; + _E_gnutls_openpgp_raw_privkey_to_gkey = raw_privkey_to_gkey; + _E_gnutls_openpgp_crt_to_gcert = key_to_gcert; + _E_gnutls_openpgp_privkey_to_gkey = privkey_to_gkey; + _E_gnutls_openpgp_crt_deinit = key_deinit; + _E_gnutls_openpgp_keyring_deinit = keyring_deinit; + _E_gnutls_openpgp_privkey_deinit = privkey_deinit; + +} diff --git a/src/daemon/https/tls/gnutls_extra_hooks.h b/src/daemon/https/tls/gnutls_extra_hooks.h new file mode 100644 index 00000000..ac55d06a --- /dev/null +++ b/src/daemon/https/tls/gnutls_extra_hooks.h @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2007 Free Software Foundation + * + * Author: Simon Josefsson + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +/* This file is included by libgnutls-extra, and it will call the + _gnutls_add_openpgp_functions() function to register its OpenPGP + functions. */ + +#include + +typedef int (*_gnutls_openpgp_verify_key_func) +(const gnutls_certificate_credentials_t, + const gnutls_datum_t *, int, + unsigned int *); + +typedef time_t (*_gnutls_openpgp_crt_creation_time_func) +(const gnutls_datum_t *); + +typedef time_t (*_gnutls_openpgp_crt_expiration_time_func) +(const gnutls_datum_t *); + +typedef int (*_gnutls_openpgp_crt_request_func) +(gnutls_session_t, gnutls_datum_t *, + const gnutls_certificate_credentials_t, + opaque *, int); + +typedef int (*_gnutls_openpgp_fingerprint_func) +(const gnutls_datum_t *, + unsigned char *, size_t *); + +typedef int (*_gnutls_openpgp_raw_key_to_gcert_func) +(gnutls_cert *, + const gnutls_datum_t *); +typedef int (*_gnutls_openpgp_raw_privkey_to_gkey_func) +(gnutls_privkey *, + const gnutls_datum_t *, + gnutls_openpgp_crt_fmt_t); + +typedef int (*_gnutls_openpgp_crt_to_gcert_func) +(gnutls_cert *, gnutls_openpgp_crt_t); + +typedef int (*_gnutls_openpgp_privkey_to_gkey_func) +(gnutls_privkey *, + gnutls_openpgp_privkey_t); + +typedef void (*_gnutls_openpgp_crt_deinit_func) +(gnutls_openpgp_crt_t); + +typedef void (*_gnutls_openpgp_keyring_deinit_func) +(gnutls_openpgp_keyring_t); + +typedef void (*_gnutls_openpgp_privkey_deinit_func) +(gnutls_openpgp_privkey_t); + +/* These are defined in libgnutls, but not exported from libgnutls, + and not intended to be used by libgnutls-extra or elsewhere. They + are declared here, because this file is included by auth_cert.c and + gnutls_cert.c too. */ +extern _gnutls_openpgp_verify_key_func _E_gnutls_openpgp_verify_key; +extern _gnutls_openpgp_crt_creation_time_func +_E_gnutls_openpgp_get_raw_key_creation_time; +extern _gnutls_openpgp_crt_expiration_time_func +_E_gnutls_openpgp_get_raw_key_expiration_time; +extern _gnutls_openpgp_fingerprint_func _E_gnutls_openpgp_fingerprint; +extern _gnutls_openpgp_crt_request_func _E_gnutls_openpgp_request_key; +extern _gnutls_openpgp_raw_key_to_gcert_func _E_gnutls_openpgp_raw_key_to_gcert; +extern _gnutls_openpgp_raw_privkey_to_gkey_func _E_gnutls_openpgp_raw_privkey_to_gkey; +extern _gnutls_openpgp_crt_to_gcert_func _E_gnutls_openpgp_crt_to_gcert; +extern _gnutls_openpgp_privkey_to_gkey_func _E_gnutls_openpgp_privkey_to_gkey; +extern _gnutls_openpgp_crt_deinit_func _E_gnutls_openpgp_crt_deinit; +extern _gnutls_openpgp_keyring_deinit_func _E_gnutls_openpgp_keyring_deinit; +extern _gnutls_openpgp_privkey_deinit_func _E_gnutls_openpgp_privkey_deinit; + +extern void _gnutls_add_openpgp_functions +(_gnutls_openpgp_verify_key_func verify_key, + _gnutls_openpgp_crt_creation_time_func key_creation_time, + _gnutls_openpgp_crt_expiration_time_func key_expiration_time, + _gnutls_openpgp_fingerprint_func fingerprint, + _gnutls_openpgp_crt_request_func request_key, + _gnutls_openpgp_raw_key_to_gcert_func raw_key_to_gcert, + _gnutls_openpgp_raw_privkey_to_gkey_func raw_privkey_to_gkey, + _gnutls_openpgp_crt_to_gcert_func key_to_gcert, + _gnutls_openpgp_privkey_to_gkey_func privkey_to_gkey, + _gnutls_openpgp_crt_deinit_func key_deinit, + _gnutls_openpgp_keyring_deinit_func keyring_deinit, + _gnutls_openpgp_privkey_deinit_func privkey_deinit); diff --git a/src/daemon/https/tls/gnutls_global.c b/src/daemon/https/tls/gnutls_global.c new file mode 100644 index 00000000..d019dcda --- /dev/null +++ b/src/daemon/https/tls/gnutls_global.c @@ -0,0 +1,375 @@ +/* + * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +#include +#include +#include +#include + +#ifdef HAVE_WINSOCK +# include +#endif + +#include "gettext.h" + +#define gnutls_log_func LOG_FUNC + +/* created by asn1c */ +extern const ASN1_ARRAY_TYPE gnutls_asn1_tab[]; +extern const ASN1_ARRAY_TYPE pkix_asn1_tab[]; + +LOG_FUNC _gnutls_log_func; +int _gnutls_log_level = 0; /* default log level */ + +ASN1_TYPE _gnutls_pkix1_asn; +ASN1_TYPE _gnutls_gnutls_asn; + +/** + * gnutls_global_set_log_function - This function sets the logging function + * @log_func: it's a log function + * + * This is the function where you set the logging function gnutls + * is going to use. This function only accepts a character array. + * Normally you may not use this function since it is only used + * for debugging purposes. + * + * gnutls_log_func is of the form, + * void (*gnutls_log_func)( int level, const char*); + **/ +void gnutls_global_set_log_function(gnutls_log_func log_func) +{ + _gnutls_log_func = log_func; +} + +/** + * gnutls_global_set_log_level - This function sets the logging level + * @level: it's an integer from 0 to 9. + * + * This is the function that allows you to set the log level. + * The level is an integer between 0 and 9. Higher values mean + * more verbosity. The default value is 0. Larger values should + * only be used with care, since they may reveal sensitive information. + * + * Use a log level over 10 to enable all debugging options. + * + **/ +void gnutls_global_set_log_level(int level) +{ + _gnutls_log_level = level; +} + +#ifdef DEBUG +/* default logging function */ +static void +dlog (int level, const char *str) + { + fputs (str, stderr); + } +#endif + +extern gnutls_alloc_function gnutls_secure_malloc; +extern gnutls_alloc_function gnutls_malloc; +extern gnutls_free_function gnutls_free; +extern int (*_gnutls_is_secure_memory)(const void *); +extern gnutls_realloc_function gnutls_realloc; +extern char *(*gnutls_strdup)(const char *); +extern void *(*gnutls_calloc)(size_t, + size_t); + +int _gnutls_is_secure_mem_null(const void *); + +/** + * gnutls_global_set_mem_functions - This function sets the memory allocation functions + * @alloc_func: it's the default memory allocation function. Like malloc(). + * @secure_alloc_func: This is the memory allocation function that will be used for sensitive data. + * @is_secure_func: a function that returns 0 if the memory given is not secure. May be NULL. + * @realloc_func: A realloc function + * @free_func: The function that frees allocated data. Must accept a NULL pointer. + * + * This is the function were you set the memory allocation functions gnutls + * is going to use. By default the libc's allocation functions (malloc(), free()), + * are used by gnutls, to allocate both sensitive and not sensitive data. + * This function is provided to set the memory allocation functions to + * something other than the defaults (ie the gcrypt allocation functions). + * + * This function must be called before gnutls_global_init() is called. + * + **/ +void gnutls_global_set_mem_functions(gnutls_alloc_function alloc_func, + gnutls_alloc_function + secure_alloc_func, + gnutls_is_secure_function + is_secure_func, + gnutls_realloc_function realloc_func, + gnutls_free_function free_func) +{ + gnutls_secure_malloc = secure_alloc_func; + gnutls_malloc = alloc_func; + gnutls_realloc = realloc_func; + gnutls_free = free_func; + + if (is_secure_func != NULL) + _gnutls_is_secure_memory = is_secure_func; + else + _gnutls_is_secure_memory = _gnutls_is_secure_mem_null; + + /* if using the libc's default malloc + * use libc's calloc as well. + */ + if (gnutls_malloc == malloc) + { + gnutls_calloc = calloc; + } + else + { /* use the included ones */ + gnutls_calloc = _gnutls_calloc; + } + gnutls_strdup = _gnutls_strdup; + +} + +#ifdef DEBUG +static void +_gnutls_gcry_log_handler (void *dummy, int level, + const char *fmt, va_list list) + { + _gnutls_log (fmt, list); + } +#endif + +static int _gnutls_init = 0; + +/** + * gnutls_global_init - This function initializes the global data to defaults. + * + * This function initializes the global data to defaults. + * Every gnutls application has a global data which holds common parameters + * shared by gnutls session structures. + * You must call gnutls_global_deinit() when gnutls usage is no longer needed + * Returns zero on success. + * + * Note that this function will also initialize libgcrypt, if it has not + * been initialized before. Thus if you want to manually initialize libgcrypt + * you must do it before calling this function. This is useful in cases you + * want to disable libgcrypt's internal lockings etc. + * + * This function increment a global counter, so that + * gnutls_global_deinit() only releases resources when it has been + * called as many times as gnutls_global_init(). This is useful when + * GnuTLS is used by more than one library in an application. This + * function can be called many times, but will only do something the + * first time. + * + * Note! This function is not thread safe. If two threads call this + * function simultaneously, they can cause a race between checking + * the global counter and incrementing it, causing both threads to + * execute the library initialization code. That would lead to a + * memory leak. To handle this, your application could invoke this + * function after aquiring a thread mutex. To ignore the potential + * memory leak is also an option. + * + **/ +int gnutls_global_init(void) +{ + int result = 0; + int res; + char c; + + if (_gnutls_init++) + return; + +#if HAVE_WINSOCK + { + WORD requested; + WSADATA data; + int err; + + requested = MAKEWORD (1, 1); + err = WSAStartup (requested, &data); + if (err != 0) + { + _gnutls_debug_log ("WSAStartup failed: %d.\n", err); + return GNUTLS_E_LIBRARY_VERSION_MISMATCH; + } + + if (data.wVersion < requested) + { + _gnutls_debug_log ("WSAStartup version check failed (%d < %d).\n", + data.wVersion, requested); + WSACleanup (); + return GNUTLS_E_LIBRARY_VERSION_MISMATCH; + } + } +#endif + + // TODO rm ? bindtextdomain(PACKAGE, LOCALEDIR); + + if (gcry_control(GCRYCTL_ANY_INITIALIZATION_P) == 0) + { + /* for gcrypt in order to be able to allocate memory */ + gcry_set_allocation_handler(gnutls_malloc, gnutls_secure_malloc, + _gnutls_is_secure_memory, gnutls_realloc, + gnutls_free); + + /* gcry_control (GCRYCTL_DISABLE_INTERNAL_LOCKING, NULL, 0); */ + + gcry_control(GCRYCTL_INITIALIZATION_FINISHED, NULL, 0); + +#ifdef DEBUG + /* applications may want to override that, so we only use + * it in debugging mode. + */ + gcry_set_log_handler (_gnutls_gcry_log_handler, NULL); +#endif + } + + if (gc_init() != GC_OK) + { + gnutls_assert (); + _gnutls_debug_log ("Initializing crypto backend failed\n"); + return GNUTLS_E_INCOMPATIBLE_CRYPTO_LIBRARY; + } + +#ifdef DEBUG + gnutls_global_set_log_function (dlog); +#endif + + /* initialize parser + * This should not deal with files in the final + * version. + */ + + res = asn1_array2tree(pkix_asn1_tab, &_gnutls_pkix1_asn, NULL); + if (res != ASN1_SUCCESS) + { + result = _gnutls_asn2err(res); + return result; + } + + res = asn1_array2tree(gnutls_asn1_tab, &_gnutls_gnutls_asn, NULL); + if (res != ASN1_SUCCESS) + { + asn1_delete_structure(&_gnutls_pkix1_asn); + result = _gnutls_asn2err(res); + return result; + } + + /* Initialize the gcrypt (if used random generator) */ + gc_pseudo_random(&c, 1); + + return result; +} + +/** + * gnutls_global_deinit - This function deinitializes the global data + * + * This function deinitializes the global data, that were initialized + * using gnutls_global_init(). + * + * Note! This function is not thread safe. See the discussion for + * gnutls_global_init() for more information. + * + **/ +void gnutls_global_deinit(void) +{ + if (_gnutls_init == 1) + { +#if HAVE_WINSOCK + WSACleanup (); +#endif + asn1_delete_structure(&_gnutls_gnutls_asn); + asn1_delete_structure(&_gnutls_pkix1_asn); + gc_done(); + } + _gnutls_init--; +} + +/* These functions should be elsewere. Kept here for + * historical reasons. + */ + +/** + * gnutls_transport_set_pull_function - This function sets a read like function + * @pull_func: a callback function similar to read() + * @session: gnutls session + * + * This is the function where you set a function for gnutls + * to receive data. Normally, if you use berkeley style sockets, + * do not need to use this function since the default (recv(2)) will + * probably be ok. + * + * PULL_FUNC is of the form, + * ssize_t (*gnutls_pull_func)(gnutls_transport_ptr_t, void*, size_t); + **/ +void gnutls_transport_set_pull_function(gnutls_session_t session, + gnutls_pull_func pull_func) +{ + session->internals._gnutls_pull_func = pull_func; +} + +/** + * gnutls_transport_set_push_function - This function sets the function to send data + * @push_func: a callback function similar to write() + * @session: gnutls session + * + * This is the function where you set a push function for gnutls + * to use in order to send data. If you are going to use berkeley style + * sockets, you do not need to use this function since + * the default (send(2)) will probably be ok. Otherwise you should + * specify this function for gnutls to be able to send data. + * + * PUSH_FUNC is of the form, + * ssize_t (*gnutls_push_func)(gnutls_transport_ptr_t, const void*, size_t); + **/ +void gnutls_transport_set_push_function(gnutls_session_t session, + gnutls_push_func push_func) +{ + session->internals._gnutls_push_func = push_func; +} + +#include + +/** + * gnutls_check_version - This function checks the library's version + * @req_version: the version to check + * + * Check that the version of the library is at minimum the requested one + * and return the version string; return NULL if the condition is not + * satisfied. If a NULL is passed to this function, no check is done, + * but the version string is simply returned. + * + * See %LIBGNUTLS_VERSION for a suitable @req_version string. + * + * Return value: Version string of run-time library, or NULL if the + * run-time library does not meet the required version number. If + * %NULL is passed to this function no check is done and only the + * version string is returned. + **/ +const char * gnutls_check_version(const char *req_version) +{ + if (!req_version || strverscmp(req_version, VERSION) <= 0) + return VERSION; + + return NULL; +} diff --git a/src/daemon/https/tls/gnutls_global.h b/src/daemon/https/tls/gnutls_global.h new file mode 100644 index 00000000..3305ebad --- /dev/null +++ b/src/daemon/https/tls/gnutls_global.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +#ifndef GNUTLS_GLOBAL_H +# define GNUTLS_GLOBAL_H + +#include + +int gnutls_is_secure_memory (const void *mem); + +extern ASN1_TYPE _gnutls_pkix1_asn; +extern ASN1_TYPE _gnutls_gnutls_asn; + +/* removed const from node_asn* to + * prevent warnings, since libtasn1 doesn't + * use the const keywork in its functions. + */ +#define _gnutls_get_gnutls_asn() ((node_asn*) _gnutls_gnutls_asn) +#define _gnutls_get_pkix() ((node_asn*) _gnutls_pkix1_asn) + +#endif diff --git a/src/daemon/https/tls/gnutls_handshake.c b/src/daemon/https/tls/gnutls_handshake.c new file mode 100644 index 00000000..1af07a91 --- /dev/null +++ b/src/daemon/https/tls/gnutls_handshake.c @@ -0,0 +1,3029 @@ +/* + * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +/* Functions that relate to the TLS handshake procedure. + */ + +#include "gnutls_int.h" +#include "gnutls_errors.h" +#include "gnutls_dh.h" +#include "debug.h" +#include "gnutls_algorithms.h" +#include "gnutls_compress.h" +#include "gnutls_cipher.h" +#include "gnutls_buffers.h" +#include "gnutls_kx.h" +#include "gnutls_handshake.h" +#include "gnutls_num.h" +#include "gnutls_hash_int.h" +#include "gnutls_db.h" +#include "gnutls_extensions.h" +#include "gnutls_supplemental.h" +#include "gnutls_auth_int.h" +#include "gnutls_v2_compat.h" +#include "auth_cert.h" +#include "gnutls_cert.h" +#include "gnutls_constate.h" +#include +#include +#include /* for gnutls_get_rsa_params() */ +#include /* for gnutls_anon_server_credentials_t */ +#include + +#ifdef HANDSHAKE_DEBUG +#define ERR(x, y) _gnutls_handshake_log( "HSK[%x]: %s (%d)\n", session, x,y) +#else +#define ERR(x, y) +#endif + +#define TRUE 1 +#define FALSE 0 + +int _gnutls_server_select_comp_method (gnutls_session_t session, + opaque * data, int datalen); + + +/* Clears the handshake hash buffers and handles. + */ +inline static void +_gnutls_handshake_hash_buffers_clear (gnutls_session_t session) +{ + _gnutls_hash_deinit (session->internals.handshake_mac_handle_md5, NULL); + _gnutls_hash_deinit (session->internals.handshake_mac_handle_sha, NULL); + session->internals.handshake_mac_handle_md5 = NULL; + session->internals.handshake_mac_handle_sha = NULL; + _gnutls_handshake_buffer_clear (session); +} + +/* this will copy the required values for resuming to + * internals, and to security_parameters. + * this will keep as less data to security_parameters. + */ +static void +resume_copy_required_values (gnutls_session_t session) +{ + /* get the new random values */ + memcpy (session->internals.resumed_security_parameters. + server_random, + session->security_parameters.server_random, TLS_RANDOM_SIZE); + memcpy (session->internals.resumed_security_parameters. + client_random, + session->security_parameters.client_random, TLS_RANDOM_SIZE); + + /* keep the ciphersuite and compression + * That is because the client must see these in our + * hello message. + */ + memcpy (session->security_parameters.current_cipher_suite. + suite, + session->internals.resumed_security_parameters. + current_cipher_suite.suite, 2); + + session->internals.compression_method = + session->internals.resumed_security_parameters.read_compression_algorithm; + /* or write_compression_algorithm + * they are the same + */ + + session->security_parameters.entity = + session->internals.resumed_security_parameters.entity; + + _gnutls_set_current_version (session, + session->internals. + resumed_security_parameters.version); + + session->security_parameters.cert_type = + session->internals.resumed_security_parameters.cert_type; + + memcpy (session->security_parameters.session_id, + session->internals.resumed_security_parameters. + session_id, sizeof (session->security_parameters.session_id)); + session->security_parameters.session_id_size = + session->internals.resumed_security_parameters.session_id_size; +} + +void +_gnutls_set_server_random (gnutls_session_t session, uint8_t * rnd) +{ + memcpy (session->security_parameters.server_random, rnd, TLS_RANDOM_SIZE); +} + +void +_gnutls_set_client_random (gnutls_session_t session, uint8_t * rnd) +{ + memcpy (session->security_parameters.client_random, rnd, TLS_RANDOM_SIZE); +} + +/* Calculate The SSL3 Finished message + */ +#define SSL3_CLIENT_MSG "CLNT" +#define SSL3_SERVER_MSG "SRVR" +#define SSL_MSG_LEN 4 +static int +_gnutls_ssl3_finished (gnutls_session_t session, int type, opaque * ret) +{ + const int siz = SSL_MSG_LEN; + mac_hd_t td_md5; + mac_hd_t td_sha; + const char *mesg; + + td_md5 = _gnutls_hash_copy (session->internals.handshake_mac_handle_md5); + if (td_md5 == NULL) + { + gnutls_assert (); + return GNUTLS_E_HASH_FAILED; + } + + td_sha = _gnutls_hash_copy (session->internals.handshake_mac_handle_sha); + if (td_sha == NULL) + { + gnutls_assert (); + _gnutls_hash_deinit (td_md5, NULL); + return GNUTLS_E_HASH_FAILED; + } + + if (type == GNUTLS_SERVER) + { + mesg = SSL3_SERVER_MSG; + } + else + { + mesg = SSL3_CLIENT_MSG; + } + + _gnutls_hash (td_md5, mesg, siz); + _gnutls_hash (td_sha, mesg, siz); + + _gnutls_mac_deinit_ssl3_handshake (td_md5, ret, + session->security_parameters. + master_secret, TLS_MASTER_SIZE); + _gnutls_mac_deinit_ssl3_handshake (td_sha, &ret[16], + session->security_parameters. + master_secret, TLS_MASTER_SIZE); + + return 0; +} + +/* Hash the handshake messages as required by TLS 1.0 + */ +#define SERVER_MSG "server finished" +#define CLIENT_MSG "client finished" +#define TLS_MSG_LEN 15 +int +_gnutls_finished (gnutls_session_t session, int type, void *ret) +{ + const int siz = TLS_MSG_LEN; + opaque concat[36]; + size_t len; + const char *mesg; + mac_hd_t td_md5 = NULL; + mac_hd_t td_sha; + gnutls_protocol_t ver = gnutls_protocol_get_version (session); + + if (ver < GNUTLS_TLS1_2) + { + td_md5 = + _gnutls_hash_copy (session->internals.handshake_mac_handle_md5); + if (td_md5 == NULL) + { + gnutls_assert (); + return GNUTLS_E_HASH_FAILED; + } + } + + td_sha = _gnutls_hash_copy (session->internals.handshake_mac_handle_sha); + if (td_sha == NULL) + { + gnutls_assert (); + _gnutls_hash_deinit (td_md5, NULL); + return GNUTLS_E_HASH_FAILED; + } + + if (ver < GNUTLS_TLS1_2) + { + _gnutls_hash_deinit (td_md5, concat); + _gnutls_hash_deinit (td_sha, &concat[16]); + len = 20 + 16; + } + else + { + _gnutls_hash_deinit (td_sha, concat); + len = 20; + } + + if (type == GNUTLS_SERVER) + { + mesg = SERVER_MSG; + } + else + { + mesg = CLIENT_MSG; + } + + return _gnutls_PRF (session, session->security_parameters.master_secret, + TLS_MASTER_SIZE, mesg, siz, concat, len, 12, ret); +} + +/* this function will produce TLS_RANDOM_SIZE==32 bytes of random data + * and put it to dst. + */ +int +_gnutls_tls_create_random (opaque * dst) +{ + uint32_t tim; + + /* Use weak random numbers for the most of the + * buffer except for the first 4 that are the + * system's time. + */ + + tim = time (NULL); + /* generate server random value */ + _gnutls_write_uint32 (tim, dst); + + if (gc_nonce (&dst[4], TLS_RANDOM_SIZE - 4) != GC_OK) + { + gnutls_assert (); + return GNUTLS_E_RANDOM_FAILED; + } + + return 0; +} + +/* returns the 0 on success or a negative value. + */ +int +_gnutls_negotiate_version (gnutls_session_t session, + gnutls_protocol_t adv_version) +{ + int ret; + + /* if we do not support that version */ + if (_gnutls_version_is_supported (session, adv_version) == 0) + { + /* If he requested something we do not support + * then we send him the highest we support. + */ + ret = _gnutls_version_max (session); + if (ret == GNUTLS_VERSION_UNKNOWN) + { + /* this check is not really needed. + */ + gnutls_assert (); + return GNUTLS_E_UNKNOWN_CIPHER_SUITE; + } + } + else + { + ret = adv_version; + } + + _gnutls_set_current_version (session, ret); + + return ret; +} + +int +_gnutls_user_hello_func (gnutls_session session, + gnutls_protocol_t adv_version) +{ + int ret; + + if (session->internals.user_hello_func != NULL) + { + ret = session->internals.user_hello_func (session); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + /* Here we need to renegotiate the version since the callee might + * have disabled some TLS versions. + */ + ret = _gnutls_negotiate_version (session, adv_version); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + } + return 0; +} + +/* Read a client hello packet. + * A client hello must be a known version client hello + * or version 2.0 client hello (only for compatibility + * since SSL version 2.0 is not supported). + */ +int +_gnutls_read_client_hello (gnutls_session_t session, opaque * data, + int datalen) +{ + uint8_t session_id_len; + int pos = 0, ret; + uint16_t suite_size, comp_size; + gnutls_protocol_t adv_version; + int neg_version; + int len = datalen; + opaque rnd[TLS_RANDOM_SIZE], *suite_ptr, *comp_ptr; + + if (session->internals.v2_hello != 0) + { /* version 2.0 */ + return _gnutls_read_client_hello_v2 (session, data, datalen); + } + DECR_LEN (len, 2); + + _gnutls_handshake_log ("HSK[%x]: Client's version: %d.%d\n", session, + data[pos], data[pos + 1]); + + adv_version = _gnutls_version_get (data[pos], data[pos + 1]); + set_adv_version (session, data[pos], data[pos + 1]); + pos += 2; + + neg_version = _gnutls_negotiate_version (session, adv_version); + if (neg_version < 0) + { + gnutls_assert (); + return ret; + } + + /* Read client random value. + */ + DECR_LEN (len, TLS_RANDOM_SIZE); + _gnutls_set_client_random (session, &data[pos]); + pos += TLS_RANDOM_SIZE; + + _gnutls_tls_create_random (rnd); + _gnutls_set_server_random (session, rnd); + + session->security_parameters.timestamp = time (NULL); + + DECR_LEN (len, 1); + session_id_len = data[pos++]; + + /* RESUME SESSION + */ + if (session_id_len > TLS_MAX_SESSION_ID_SIZE) + { + gnutls_assert (); + return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; + } + DECR_LEN (len, session_id_len); + + ret = _gnutls_server_restore_session (session, &data[pos], session_id_len); + pos += session_id_len; + + if (ret == 0) + { /* resumed! */ + resume_copy_required_values (session); + session->internals.resumed = RESUME_TRUE; + return _gnutls_user_hello_func (session, adv_version); + } + else + { + _gnutls_generate_session_id (session->security_parameters. + session_id, + &session->security_parameters. + session_id_size); + + session->internals.resumed = RESUME_FALSE; + } + + /* Remember ciphersuites for later + */ + DECR_LEN (len, 2); + suite_size = _gnutls_read_uint16 (&data[pos]); + pos += 2; + + DECR_LEN (len, suite_size); + suite_ptr = &data[pos]; + pos += suite_size; + + /* Point to the compression methods + */ + DECR_LEN (len, 1); + comp_size = data[pos++]; /* z is the number of compression methods */ + + DECR_LEN (len, comp_size); + comp_ptr = &data[pos]; + pos += comp_size; + + /* Parse the extensions (if any) + */ + if (neg_version >= GNUTLS_TLS1) + { + ret = _gnutls_parse_extensions (session, EXTENSION_APPLICATION, &data[pos], len); /* len is the rest of the parsed length */ + if (ret < 0) + { + gnutls_assert (); + return ret; + } + } + + ret = _gnutls_user_hello_func (session, adv_version); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + if (neg_version >= GNUTLS_TLS1) + { + ret = _gnutls_parse_extensions (session, EXTENSION_TLS, &data[pos], len); /* len is the rest of the parsed length */ + if (ret < 0) + { + gnutls_assert (); + return ret; + } + } + + /* select an appropriate cipher suite + */ + ret = _gnutls_server_select_suite (session, suite_ptr, suite_size); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + /* select appropriate compression method */ + ret = _gnutls_server_select_comp_method (session, comp_ptr, comp_size); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + return 0; +} + +/* here we hash all pending data. + */ +inline static int +_gnutls_handshake_hash_pending (gnutls_session_t session) +{ + size_t siz; + int ret; + opaque *data; + + if (session->internals.handshake_mac_handle_sha == NULL || + session->internals.handshake_mac_handle_md5 == NULL) + { + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + /* We check if there are pending data to hash. + */ + if ((ret = _gnutls_handshake_buffer_get_ptr (session, &data, &siz)) < 0) + { + gnutls_assert (); + return ret; + } + + if (siz > 0) + { + _gnutls_hash (session->internals.handshake_mac_handle_sha, data, siz); + _gnutls_hash (session->internals.handshake_mac_handle_md5, data, siz); + } + + _gnutls_handshake_buffer_empty (session); + + return 0; +} + + +/* This is to be called after sending CHANGE CIPHER SPEC packet + * and initializing encryption. This is the first encrypted message + * we send. + */ +int +_gnutls_send_finished (gnutls_session_t session, int again) +{ + uint8_t data[36]; + int ret; + int data_size = 0; + + + if (again == 0) + { + + /* This is needed in order to hash all the required + * messages. + */ + if ((ret = _gnutls_handshake_hash_pending (session)) < 0) + { + gnutls_assert (); + return ret; + } + + if (gnutls_protocol_get_version (session) == GNUTLS_SSL3) + { + ret = + _gnutls_ssl3_finished (session, + session->security_parameters.entity, data); + data_size = 36; + } + else + { /* TLS 1.0 */ + ret = + _gnutls_finished (session, + session->security_parameters.entity, data); + data_size = 12; + } + + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + } + + ret = + _gnutls_send_handshake (session, data, data_size, + GNUTLS_HANDSHAKE_FINISHED); + + return ret; +} + +/* This is to be called after sending our finished message. If everything + * went fine we have negotiated a secure connection + */ +int +_gnutls_recv_finished (gnutls_session_t session) +{ + uint8_t data[36], *vrfy; + int data_size; + int ret; + int vrfysize; + + ret = + _gnutls_recv_handshake (session, &vrfy, &vrfysize, + GNUTLS_HANDSHAKE_FINISHED, MANDATORY_PACKET); + if (ret < 0) + { + ERR ("recv finished int", ret); + gnutls_assert (); + return ret; + } + + + if (gnutls_protocol_get_version (session) == GNUTLS_SSL3) + { + data_size = 36; + } + else + { + data_size = 12; + } + + if (vrfysize != data_size) + { + gnutls_assert (); + gnutls_free (vrfy); + return GNUTLS_E_ERROR_IN_FINISHED_PACKET; + } + + if (gnutls_protocol_get_version (session) == GNUTLS_SSL3) + { + ret = + _gnutls_ssl3_finished (session, + (session->security_parameters. + entity + 1) % 2, data); + } + else + { /* TLS 1.0 */ + ret = + _gnutls_finished (session, + (session->security_parameters.entity + + 1) % 2, data); + } + + if (ret < 0) + { + gnutls_assert (); + gnutls_free (vrfy); + return ret; + } + + if (memcmp (vrfy, data, data_size) != 0) + { + gnutls_assert (); + ret = GNUTLS_E_ERROR_IN_FINISHED_PACKET; + } + gnutls_free (vrfy); + + return ret; +} + +/* returns PK_RSA if the given cipher suite list only supports, + * RSA algorithms, PK_DSA if DSS, and PK_ANY for both or PK_NONE for none. + */ +static int +_gnutls_server_find_pk_algos_in_ciphersuites (const opaque * + data, int datalen) +{ + int j; + gnutls_pk_algorithm_t algo = GNUTLS_PK_NONE, prev_algo = 0; + gnutls_kx_algorithm_t kx; + cipher_suite_st cs; + + if (datalen % 2 != 0) + { + gnutls_assert (); + return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; + } + + for (j = 0; j < datalen; j += 2) + { + memcpy (&cs.suite, &data[j], 2); + kx = _gnutls_cipher_suite_get_kx_algo (&cs); + + if (_gnutls_map_kx_get_cred (kx, 1) == GNUTLS_CRD_CERTIFICATE) + { + algo = _gnutls_map_pk_get_pk (kx); + + if (algo != prev_algo && prev_algo != 0) + return GNUTLS_PK_ANY; + prev_algo = algo; + } + } + + return algo; +} + + +/* This selects the best supported ciphersuite from the given ones. Then + * it adds the suite to the session and performs some checks. + */ +int +_gnutls_server_select_suite (gnutls_session_t session, opaque * data, + int datalen) +{ + int x, i, j; + cipher_suite_st *ciphers, cs; + int retval, err; + gnutls_pk_algorithm_t pk_algo; /* will hold the pk algorithms + * supported by the peer. + */ + + pk_algo = _gnutls_server_find_pk_algos_in_ciphersuites (data, datalen); + + x = _gnutls_supported_ciphersuites (session, &ciphers); + if (x < 0) + { /* the case x==0 is handled within the function. */ + gnutls_assert (); + return x; + } + + /* Here we remove any ciphersuite that does not conform + * the certificate requested, or to the + * authentication requested (e.g. SRP). + */ + x = _gnutls_remove_unwanted_ciphersuites (session, &ciphers, x, pk_algo); + if (x <= 0) + { + gnutls_assert (); + gnutls_free (ciphers); + if (x < 0) + return x; + else + return GNUTLS_E_UNKNOWN_CIPHER_SUITE; + } + + /* Data length should be zero mod 2 since + * every ciphersuite is 2 bytes. (this check is needed + * see below). + */ + if (datalen % 2 != 0) + { + gnutls_assert (); + return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; + } +#ifdef HANDSHAKE_DEBUG + + _gnutls_handshake_log ("HSK[%x]: Requested cipher suites: \n", session); + for (j = 0; j < datalen; j += 2) + { + memcpy (&cs.suite, &data[j], 2); + _gnutls_handshake_log ("\t%s\n", _gnutls_cipher_suite_get_name (&cs)); + } + _gnutls_handshake_log ("HSK[%x]: Supported cipher suites: \n", session); + for (j = 0; j < x; j++) + _gnutls_handshake_log ("\t%s\n", + _gnutls_cipher_suite_get_name (&ciphers[j])); +#endif + memset (session->security_parameters.current_cipher_suite.suite, '\0', 2); + + retval = GNUTLS_E_UNKNOWN_CIPHER_SUITE; + + for (j = 0; j < datalen; j += 2) + { + for (i = 0; i < x; i++) + { + if (memcmp (ciphers[i].suite, &data[j], 2) == 0) + { + memcpy (&cs.suite, &data[j], 2); + + _gnutls_handshake_log + ("HSK[%x]: Selected cipher suite: %s\n", session, + _gnutls_cipher_suite_get_name (&cs)); + memcpy (session->security_parameters.current_cipher_suite. + suite, ciphers[i].suite, 2); + retval = 0; + goto finish; + } + } + } + +finish: + gnutls_free (ciphers); + + if (retval != 0) + { + gnutls_assert (); + return retval; + } + + /* check if the credentials (username, public key etc.) are ok + */ + if (_gnutls_get_kx_cred + (session, + _gnutls_cipher_suite_get_kx_algo (&session->security_parameters. + current_cipher_suite), + &err) == NULL && err != 0) + { + gnutls_assert (); + return GNUTLS_E_INSUFFICIENT_CREDENTIALS; + } + + + /* set the mod_auth_st to the appropriate struct + * according to the KX algorithm. This is needed since all the + * handshake functions are read from there; + */ + session->internals.auth_struct = + _gnutls_kx_auth_struct (_gnutls_cipher_suite_get_kx_algo + (&session->security_parameters. + current_cipher_suite)); + if (session->internals.auth_struct == NULL) + { + + _gnutls_handshake_log + ("HSK[%x]: Cannot find the appropriate handler for the KX algorithm\n", + session); + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + return 0; + +} + + +/* This selects the best supported compression method from the ones provided + */ +int +_gnutls_server_select_comp_method (gnutls_session_t session, + opaque * data, int datalen) +{ + int x, i, j; + uint8_t *comps; + + x = _gnutls_supported_compression_methods (session, &comps); + if (x < 0) + { + gnutls_assert (); + return x; + } + + memset (&session->internals.compression_method, 0, + sizeof (gnutls_compression_method_t)); + + for (j = 0; j < datalen; j++) + { + for (i = 0; i < x; i++) + { + if (comps[i] == data[j]) + { + gnutls_compression_method_t method = + _gnutls_compression_get_id (comps[i]); + + session->internals.compression_method = method; + gnutls_free (comps); + + _gnutls_handshake_log + ("HSK[%x]: Selected Compression Method: %s\n", session, + gnutls_compression_get_name (session->internals. + compression_method)); + + + return 0; + } + } + } + + /* we were not able to find a compatible compression + * algorithm + */ + gnutls_free (comps); + gnutls_assert (); + return GNUTLS_E_UNKNOWN_COMPRESSION_ALGORITHM; + +} + +/* This function sends an empty handshake packet. (like hello request). + * If the previous _gnutls_send_empty_handshake() returned + * GNUTLS_E_AGAIN or GNUTLS_E_INTERRUPTED, then it must be called again + * (until it returns ok), with NULL parameters. + */ +int +_gnutls_send_empty_handshake (gnutls_session_t session, + gnutls_handshake_description_t type, int again) +{ + opaque data = 0; + opaque *ptr; + + if (again == 0) + ptr = &data; + else + ptr = NULL; + + return _gnutls_send_handshake (session, ptr, 0, type); +} + + +/* This function will hash the handshake message we sent. + */ +static int +_gnutls_handshake_hash_add_sent (gnutls_session_t session, + gnutls_handshake_description_t type, + opaque * dataptr, uint32_t datalen) +{ + int ret; + + if ((ret = _gnutls_handshake_hash_pending (session)) < 0) + { + gnutls_assert (); + return ret; + } + + if (type != GNUTLS_HANDSHAKE_HELLO_REQUEST) + { + _gnutls_hash (session->internals.handshake_mac_handle_sha, dataptr, + datalen); + _gnutls_hash (session->internals.handshake_mac_handle_md5, dataptr, + datalen); + } + + return 0; +} + + +/* This function sends a handshake message of type 'type' containing the + * data specified here. If the previous _gnutls_send_handshake() returned + * GNUTLS_E_AGAIN or GNUTLS_E_INTERRUPTED, then it must be called again + * (until it returns ok), with NULL parameters. + */ +int +_gnutls_send_handshake (gnutls_session_t session, void *i_data, + uint32_t i_datasize, + gnutls_handshake_description_t type) +{ + int ret; + uint8_t *data; + uint32_t datasize; + int pos = 0; + + if (i_data == NULL && i_datasize == 0) + { + /* we are resuming a previously interrupted + * send. + */ + ret = _gnutls_handshake_io_write_flush (session); + return ret; + + } + + if (i_data == NULL && i_datasize > 0) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + /* first run */ + datasize = i_datasize + HANDSHAKE_HEADER_SIZE; + data = gnutls_alloca (datasize); + if (data == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + data[pos++] = (uint8_t) type; + _gnutls_write_uint24 (i_datasize, &data[pos]); + pos += 3; + + if (i_datasize > 0) + memcpy (&data[pos], i_data, i_datasize); + + _gnutls_handshake_log ("HSK[%x]: %s was send [%ld bytes]\n", + session, _gnutls_handshake2str (type), datasize); + + + /* Here we keep the handshake messages in order to hash them... + */ + if (type != GNUTLS_HANDSHAKE_HELLO_REQUEST) + if ((ret = + _gnutls_handshake_hash_add_sent (session, type, data, datasize)) < 0) + { + gnutls_assert (); + gnutls_afree (data); + return ret; + } + + session->internals.last_handshake_out = type; + + ret = + _gnutls_handshake_io_send_int (session, GNUTLS_HANDSHAKE, type, + data, datasize); + + gnutls_afree (data); + + return ret; +} + +/* This function will read the handshake header and return it to the caller. If the + * received handshake packet is not the one expected then it buffers the header, and + * returns UNEXPECTED_HANDSHAKE_PACKET. + * + * FIXME: This function is complex. + */ +#define SSL2_HEADERS 1 +static int +_gnutls_recv_handshake_header (gnutls_session_t session, + gnutls_handshake_description_t type, + gnutls_handshake_description_t * recv_type) +{ + int ret; + uint32_t length32 = 0; + uint8_t *dataptr = NULL; /* for realloc */ + size_t handshake_header_size = HANDSHAKE_HEADER_SIZE; + + /* if we have data into the buffer then return them, do not read the next packet. + * In order to return we need a full TLS handshake header, or in case of a version 2 + * packet, then we return the first byte. + */ + if (session->internals.handshake_header_buffer.header_size == + handshake_header_size || (session->internals.v2_hello != 0 + && type == GNUTLS_HANDSHAKE_CLIENT_HELLO + && session->internals. + handshake_header_buffer.packet_length > 0)) + { + + *recv_type = session->internals.handshake_header_buffer.recv_type; + + return session->internals.handshake_header_buffer.packet_length; + } + + /* Note: SSL2_HEADERS == 1 */ + + dataptr = session->internals.handshake_header_buffer.header; + + /* If we haven't already read the handshake headers. + */ + if (session->internals.handshake_header_buffer.header_size < SSL2_HEADERS) + { + ret = + _gnutls_handshake_io_recv_int (session, GNUTLS_HANDSHAKE, + type, dataptr, SSL2_HEADERS); + + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + /* The case ret==0 is caught here. + */ + if (ret != SSL2_HEADERS) + { + gnutls_assert (); + return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; + } + session->internals.handshake_header_buffer.header_size = SSL2_HEADERS; + } + + if (session->internals.v2_hello == 0 + || type != GNUTLS_HANDSHAKE_CLIENT_HELLO) + { + ret = + _gnutls_handshake_io_recv_int (session, GNUTLS_HANDSHAKE, + type, + &dataptr[session-> + internals. + handshake_header_buffer. + header_size], + HANDSHAKE_HEADER_SIZE - + session->internals. + handshake_header_buffer.header_size); + if (ret <= 0) + { + gnutls_assert (); + return (ret < 0) ? ret : GNUTLS_E_UNEXPECTED_PACKET_LENGTH; + } + if ((size_t) ret != + HANDSHAKE_HEADER_SIZE - + session->internals.handshake_header_buffer.header_size) + { + gnutls_assert (); + return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; + } + *recv_type = dataptr[0]; + + /* we do not use DECR_LEN because we know + * that the packet has enough data. + */ + length32 = _gnutls_read_uint24 (&dataptr[1]); + handshake_header_size = HANDSHAKE_HEADER_SIZE; + + _gnutls_handshake_log ("HSK[%x]: %s was received [%ld bytes]\n", + session, _gnutls_handshake2str (dataptr[0]), + length32 + HANDSHAKE_HEADER_SIZE); + + } + else + { /* v2 hello */ + length32 = session->internals.v2_hello - SSL2_HEADERS; /* we've read the first byte */ + + handshake_header_size = SSL2_HEADERS; /* we've already read one byte */ + + *recv_type = dataptr[0]; + + _gnutls_handshake_log ("HSK[%x]: %s(v2) was received [%ld bytes]\n", + session, _gnutls_handshake2str (*recv_type), + length32 + handshake_header_size); + + if (*recv_type != GNUTLS_HANDSHAKE_CLIENT_HELLO) + { /* it should be one or nothing */ + gnutls_assert (); + return GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET; + } + } + + /* put the packet into the buffer */ + session->internals.handshake_header_buffer.header_size = + handshake_header_size; + session->internals.handshake_header_buffer.packet_length = length32; + session->internals.handshake_header_buffer.recv_type = *recv_type; + + if (*recv_type != type) + { + gnutls_assert (); + return GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET; + } + + return length32; +} + +#define _gnutls_handshake_header_buffer_clear( session) session->internals.handshake_header_buffer.header_size = 0 + + + +/* This function will hash the handshake headers and the + * handshake data. + */ +static int +_gnutls_handshake_hash_add_recvd (gnutls_session_t session, + gnutls_handshake_description_t recv_type, + opaque * header, uint16_t header_size, + opaque * dataptr, uint32_t datalen) +{ + int ret; + + /* The idea here is to hash the previous message we received, + * and add the one we just received into the handshake_hash_buffer. + */ + + if ((ret = _gnutls_handshake_hash_pending (session)) < 0) + { + gnutls_assert (); + return ret; + } + + /* here we buffer the handshake messages - needed at Finished message */ + if (recv_type != GNUTLS_HANDSHAKE_HELLO_REQUEST) + { + + if ((ret = + _gnutls_handshake_buffer_put (session, header, header_size)) < 0) + { + gnutls_assert (); + return ret; + } + + if (datalen > 0) + { + if ((ret = + _gnutls_handshake_buffer_put (session, dataptr, datalen)) < 0) + { + gnutls_assert (); + return ret; + } + } + } + + return 0; +} + + +/* This function will receive handshake messages of the given types, + * and will pass the message to the right place in order to be processed. + * E.g. for the SERVER_HELLO message (if it is expected), it will be + * passed to _gnutls_recv_hello(). + */ +int +_gnutls_recv_handshake (gnutls_session_t session, uint8_t ** data, + int *datalen, gnutls_handshake_description_t type, + Optional optional) +{ + int ret; + uint32_t length32 = 0; + opaque *dataptr = NULL; + gnutls_handshake_description_t recv_type; + + ret = _gnutls_recv_handshake_header (session, type, &recv_type); + if (ret < 0) + { + + if (ret == GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET + && optional == OPTIONAL_PACKET) + { + if (datalen != NULL) + *datalen = 0; + if (data != NULL) + *data = NULL; + return 0; /* ok just ignore the packet */ + } + + return ret; + } + + session->internals.last_handshake_in = recv_type; + + length32 = ret; + + if (length32 > 0) + dataptr = gnutls_malloc (length32); + else if (recv_type != GNUTLS_HANDSHAKE_SERVER_HELLO_DONE) + { + gnutls_assert (); + return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; + } + + if (dataptr == NULL && length32 > 0) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + if (datalen != NULL) + *datalen = length32; + + if (length32 > 0) + { + ret = + _gnutls_handshake_io_recv_int (session, GNUTLS_HANDSHAKE, + type, dataptr, length32); + if (ret <= 0) + { + gnutls_assert (); + gnutls_free (dataptr); + return (ret == 0) ? GNUTLS_E_UNEXPECTED_PACKET_LENGTH : ret; + } + } + + if (data != NULL && length32 > 0) + *data = dataptr; + + + ret = _gnutls_handshake_hash_add_recvd (session, recv_type, + session->internals. + handshake_header_buffer.header, + session->internals. + handshake_header_buffer. + header_size, dataptr, length32); + if (ret < 0) + { + gnutls_assert (); + _gnutls_handshake_header_buffer_clear (session); + return ret; + } + + /* If we fail before this then we will reuse the handshake header + * have have received above. if we get here the we clear the handshake + * header we received. + */ + _gnutls_handshake_header_buffer_clear (session); + + switch (recv_type) + { + case GNUTLS_HANDSHAKE_CLIENT_HELLO: + case GNUTLS_HANDSHAKE_SERVER_HELLO: + ret = _gnutls_recv_hello (session, dataptr, length32); + /* dataptr is freed because the caller does not + * need it */ + gnutls_free (dataptr); + if (data != NULL) + *data = NULL; + break; + case GNUTLS_HANDSHAKE_SERVER_HELLO_DONE: + if (length32 == 0) + ret = 0; + else + ret = GNUTLS_E_UNEXPECTED_PACKET_LENGTH; + break; + case GNUTLS_HANDSHAKE_CERTIFICATE_PKT: + case GNUTLS_HANDSHAKE_FINISHED: + case GNUTLS_HANDSHAKE_SERVER_KEY_EXCHANGE: + case GNUTLS_HANDSHAKE_CLIENT_KEY_EXCHANGE: + case GNUTLS_HANDSHAKE_CERTIFICATE_REQUEST: + case GNUTLS_HANDSHAKE_CERTIFICATE_VERIFY: + case GNUTLS_HANDSHAKE_SUPPLEMENTAL: + ret = length32; + break; + default: + gnutls_assert (); + gnutls_free (dataptr); + if (data != NULL) + *data = NULL; + ret = GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET; + } + + return ret; +} + +/* This function checks if the given cipher suite is supported, and sets it + * to the session; + */ +static int +_gnutls_client_set_ciphersuite (gnutls_session_t session, opaque suite[2]) +{ + uint8_t z; + cipher_suite_st *cipher_suites; + int cipher_suite_num; + int i, err; + + z = 1; + cipher_suite_num = _gnutls_supported_ciphersuites (session, &cipher_suites); + if (cipher_suite_num < 0) + { + gnutls_assert (); + return cipher_suite_num; + } + + for (i = 0; i < cipher_suite_num; i++) + { + if (memcmp (&cipher_suites[i], suite, 2) == 0) + { + z = 0; + break; + } + } + + gnutls_free (cipher_suites); + + if (z != 0) + { + gnutls_assert (); + return GNUTLS_E_UNKNOWN_CIPHER_SUITE; + } + + memcpy (session->security_parameters.current_cipher_suite.suite, suite, 2); + + _gnutls_handshake_log ("HSK[%x]: Selected cipher suite: %s\n", session, + _gnutls_cipher_suite_get_name (&session-> + security_parameters. + current_cipher_suite)); + + + /* check if the credentials (username, public key etc.) are ok. + * Actually checks if they exist. + */ + if (_gnutls_get_kx_cred + (session, _gnutls_cipher_suite_get_kx_algo (&session-> + security_parameters. + current_cipher_suite), + &err) == NULL && err != 0) + { + gnutls_assert (); + return GNUTLS_E_INSUFFICIENT_CREDENTIALS; + } + + + /* set the mod_auth_st to the appropriate struct + * according to the KX algorithm. This is needed since all the + * handshake functions are read from there; + */ + session->internals.auth_struct = + _gnutls_kx_auth_struct (_gnutls_cipher_suite_get_kx_algo + (&session->security_parameters. + current_cipher_suite)); + + if (session->internals.auth_struct == NULL) + { + + _gnutls_handshake_log + ("HSK[%x]: Cannot find the appropriate handler for the KX algorithm\n", + session); + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + + return 0; +} + +/* This function sets the given comp method to the session. + */ +static int +_gnutls_client_set_comp_method (gnutls_session_t session, opaque comp_method) +{ + int comp_methods_num; + uint8_t *compression_methods; + int i; + + comp_methods_num = _gnutls_supported_compression_methods (session, + &compression_methods); + if (comp_methods_num < 0) + { + gnutls_assert (); + return comp_methods_num; + } + + for (i = 0; i < comp_methods_num; i++) + { + if (compression_methods[i] == comp_method) + { + comp_methods_num = 0; + break; + } + } + + gnutls_free (compression_methods); + + if (comp_methods_num != 0) + { + gnutls_assert (); + return GNUTLS_E_UNKNOWN_COMPRESSION_ALGORITHM; + } + + session->internals.compression_method = + _gnutls_compression_get_id (comp_method); + + + return 0; +} + +/* This function returns 0 if we are resuming a session or -1 otherwise. + * This also sets the variables in the session. Used only while reading a server + * hello. + */ +static int +_gnutls_client_check_if_resuming (gnutls_session_t session, + opaque * session_id, int session_id_len) +{ + opaque buf[2 * TLS_MAX_SESSION_ID_SIZE + 1]; + + _gnutls_handshake_log ("HSK[%x]: SessionID length: %d\n", session, + session_id_len); + _gnutls_handshake_log ("HSK[%x]: SessionID: %s\n", session, + _gnutls_bin2hex (session_id, session_id_len, buf, + sizeof (buf))); + + if (session_id_len > 0 && + session->internals.resumed_security_parameters.session_id_size == + session_id_len + && memcmp (session_id, + session->internals.resumed_security_parameters. + session_id, session_id_len) == 0) + { + /* resume session */ + memcpy (session->internals. + resumed_security_parameters.server_random, + session->security_parameters.server_random, TLS_RANDOM_SIZE); + memcpy (session->internals. + resumed_security_parameters.client_random, + session->security_parameters.client_random, TLS_RANDOM_SIZE); + session->internals.resumed = RESUME_TRUE; /* we are resuming */ + + return 0; + } + else + { + /* keep the new session id */ + session->internals.resumed = RESUME_FALSE; /* we are not resuming */ + session->security_parameters.session_id_size = session_id_len; + memcpy (session->security_parameters.session_id, + session_id, session_id_len); + + return -1; + } +} + + +/* This function reads and parses the server hello handshake message. + * This function also restores resumed parameters if we are resuming a + * session. + */ +static int +_gnutls_read_server_hello (gnutls_session_t session, + opaque * data, int datalen) +{ + uint8_t session_id_len = 0; + int pos = 0; + int ret = 0; + gnutls_protocol_t version; + int len = datalen; + + if (datalen < 38) + { + gnutls_assert (); + return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; + } + + _gnutls_handshake_log ("HSK[%x]: Server's version: %d.%d\n", + session, data[pos], data[pos + 1]); + + DECR_LEN (len, 2); + version = _gnutls_version_get (data[pos], data[pos + 1]); + if (_gnutls_version_is_supported (session, version) == 0) + { + gnutls_assert (); + return GNUTLS_E_UNSUPPORTED_VERSION_PACKET; + } + else + { + _gnutls_set_current_version (session, version); + } + + pos += 2; + + DECR_LEN (len, TLS_RANDOM_SIZE); + _gnutls_set_server_random (session, &data[pos]); + pos += TLS_RANDOM_SIZE; + + + /* Read session ID + */ + DECR_LEN (len, 1); + session_id_len = data[pos++]; + + if (len < session_id_len) + { + gnutls_assert (); + return GNUTLS_E_UNSUPPORTED_VERSION_PACKET; + } + DECR_LEN (len, session_id_len); + + + /* check if we are resuming and set the appropriate + * values; + */ + if (_gnutls_client_check_if_resuming + (session, &data[pos], session_id_len) == 0) + return 0; + pos += session_id_len; + + + /* Check if the given cipher suite is supported and copy + * it to the session. + */ + + DECR_LEN (len, 2); + ret = _gnutls_client_set_ciphersuite (session, &data[pos]); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + pos += 2; + + + + /* move to compression + */ + DECR_LEN (len, 1); + + ret = _gnutls_client_set_comp_method (session, data[pos++]); + if (ret < 0) + { + gnutls_assert (); + return GNUTLS_E_UNKNOWN_COMPRESSION_ALGORITHM; + } + + /* Parse extensions. + */ + if (version >= GNUTLS_TLS1) + { + ret = _gnutls_parse_extensions (session, EXTENSION_ANY, &data[pos], len); /* len is the rest of the parsed length */ + if (ret < 0) + { + gnutls_assert (); + return ret; + } + } + return ret; +} + + +/* This function copies the appropriate ciphersuites to a locally allocated buffer + * Needed in client hello messages. Returns the new data length. + */ +static int +_gnutls_copy_ciphersuites (gnutls_session_t session, + opaque * ret_data, size_t ret_data_size) +{ + int ret, i; + cipher_suite_st *cipher_suites; + uint16_t cipher_num; + int datalen, pos; + + ret = _gnutls_supported_ciphersuites_sorted (session, &cipher_suites); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + /* Here we remove any ciphersuite that does not conform + * the certificate requested, or to the + * authentication requested (eg SRP). + */ + ret = + _gnutls_remove_unwanted_ciphersuites (session, &cipher_suites, ret, -1); + if (ret < 0) + { + gnutls_assert (); + gnutls_free (cipher_suites); + return ret; + } + + /* If no cipher suites were enabled. + */ + if (ret == 0) + { + gnutls_assert (); + gnutls_free (cipher_suites); + return GNUTLS_E_INSUFFICIENT_CREDENTIALS; + } + + cipher_num = ret; + + cipher_num *= sizeof (uint16_t); /* in order to get bytes */ + + datalen = pos = 0; + + datalen += sizeof (uint16_t) + cipher_num; + + if ((size_t) datalen > ret_data_size) + { + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + _gnutls_write_uint16 (cipher_num, ret_data); + pos += 2; + + for (i = 0; i < (cipher_num / 2); i++) + { + memcpy (&ret_data[pos], cipher_suites[i].suite, 2); + pos += 2; + } + gnutls_free (cipher_suites); + + return datalen; +} + + +/* This function copies the appropriate compression methods, to a locally allocated buffer + * Needed in hello messages. Returns the new data length. + */ +static int +_gnutls_copy_comp_methods (gnutls_session_t session, + opaque * ret_data, size_t ret_data_size) +{ + int ret, i; + uint8_t *compression_methods, comp_num; + int datalen, pos; + + ret = _gnutls_supported_compression_methods (session, &compression_methods); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + comp_num = ret; + + datalen = pos = 0; + datalen += comp_num + 1; + + if ((size_t) datalen > ret_data_size) + { + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + ret_data[pos++] = comp_num; /* put the number of compression methods */ + + for (i = 0; i < comp_num; i++) + { + ret_data[pos++] = compression_methods[i]; + } + + gnutls_free (compression_methods); + + return datalen; +} + +/* This should be sufficient by now. It should hold all the extensions + * plus the headers in a hello message. + */ +#define MAX_EXT_DATA_LENGTH 1024 + +/* This function sends the client hello handshake message. + */ +static int +_gnutls_send_client_hello (gnutls_session_t session, int again) +{ + opaque *data = NULL; + int extdatalen; + int pos = 0; + int datalen = 0, ret = 0; + opaque rnd[TLS_RANDOM_SIZE]; + gnutls_protocol_t hver; + opaque extdata[MAX_EXT_DATA_LENGTH]; + + opaque *SessionID = + session->internals.resumed_security_parameters.session_id; + uint8_t session_id_len = + session->internals.resumed_security_parameters.session_id_size; + + if (SessionID == NULL) + session_id_len = 0; + else if (session_id_len == 0) + SessionID = NULL; + + if (again == 0) + { + + datalen = 2 + (session_id_len + 1) + TLS_RANDOM_SIZE; + /* 2 for version, (4 for unix time + 28 for random bytes==TLS_RANDOM_SIZE) + */ + + data = gnutls_malloc (datalen); + if (data == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + /* if we are resuming a session then we set the + * version number to the previously established. + */ + if (SessionID == NULL) + hver = _gnutls_version_max (session); + else + { /* we are resuming a session */ + hver = session->internals.resumed_security_parameters.version; + } + + if (hver == GNUTLS_VERSION_UNKNOWN || hver == 0) + { + gnutls_assert (); + gnutls_free (data); + return GNUTLS_E_INTERNAL_ERROR; + } + + data[pos++] = _gnutls_version_get_major (hver); + data[pos++] = _gnutls_version_get_minor (hver); + + /* Set the version we advertized as maximum + * (RSA uses it). + */ + _gnutls_set_adv_version (session, hver); + + /* Some old implementations do not interoperate if we send a + * different version in the record layer. + * It seems they prefer to read the record's version + * as the one we actually requested. + * The proper behaviour is to use the one in the client hello + * handshake packet and ignore the one in the packet's record + * header. + */ + _gnutls_set_current_version (session, hver); + + /* In order to know when this session was initiated. + */ + session->security_parameters.timestamp = time (NULL); + + /* Generate random data + */ + _gnutls_tls_create_random (rnd); + _gnutls_set_client_random (session, rnd); + + memcpy (&data[pos], rnd, TLS_RANDOM_SIZE); + pos += TLS_RANDOM_SIZE; + + /* Copy the Session ID + */ + data[pos++] = session_id_len; + + if (session_id_len > 0) + { + memcpy (&data[pos], SessionID, session_id_len); + pos += session_id_len; + } + + + /* Copy the ciphersuites. + */ + extdatalen = + _gnutls_copy_ciphersuites (session, extdata, sizeof (extdata)); + if (extdatalen > 0) + { + datalen += extdatalen; + data = gnutls_realloc_fast (data, datalen); + if (data == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + memcpy (&data[pos], extdata, extdatalen); + pos += extdatalen; + + } + else + { + if (extdatalen == 0) + extdatalen = GNUTLS_E_INTERNAL_ERROR; + gnutls_free (data); + gnutls_assert (); + return extdatalen; + } + + + /* Copy the compression methods. + */ + extdatalen = + _gnutls_copy_comp_methods (session, extdata, sizeof (extdata)); + if (extdatalen > 0) + { + datalen += extdatalen; + data = gnutls_realloc_fast (data, datalen); + if (data == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + memcpy (&data[pos], extdata, extdatalen); + pos += extdatalen; + + } + else + { + if (extdatalen == 0) + extdatalen = GNUTLS_E_INTERNAL_ERROR; + gnutls_free (data); + gnutls_assert (); + return extdatalen; + } + + /* Generate and copy TLS extensions. + */ + if (hver >= GNUTLS_TLS1) + { + extdatalen = + _gnutls_gen_extensions (session, extdata, sizeof (extdata)); + + if (extdatalen > 0) + { + datalen += extdatalen; + data = gnutls_realloc_fast (data, datalen); + if (data == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + memcpy (&data[pos], extdata, extdatalen); + } + else if (extdatalen < 0) + { + gnutls_assert (); + gnutls_free (data); + return extdatalen; + } + } + } + + ret = + _gnutls_send_handshake (session, data, datalen, + GNUTLS_HANDSHAKE_CLIENT_HELLO); + gnutls_free (data); + + return ret; +} + +static int +_gnutls_send_server_hello (gnutls_session_t session, int again) +{ + opaque *data = NULL; + opaque extdata[MAX_EXT_DATA_LENGTH]; + int extdatalen; + int pos = 0; + int datalen, ret = 0; + uint8_t comp; + opaque *SessionID = session->security_parameters.session_id; + uint8_t session_id_len = session->security_parameters.session_id_size; + opaque buf[2 * TLS_MAX_SESSION_ID_SIZE + 1]; + + if (SessionID == NULL) + session_id_len = 0; + + datalen = 0; + +#ifdef ENABLE_SRP + if (IS_SRP_KX + (_gnutls_cipher_suite_get_kx_algo + (&session->security_parameters.current_cipher_suite))) + { + /* While resuming we cannot check the username extension since it is + * not available at this point. It will be copied on connection + * state activation. + */ + if (session->internals.resumed == RESUME_FALSE && + session->security_parameters.extensions.srp_username[0] == 0) + { + /* The peer didn't send a valid SRP extension with the + * SRP username. The draft requires that we send a fatal + * alert and abort. + */ + gnutls_assert (); + ret = gnutls_alert_send (session, GNUTLS_AL_FATAL, + GNUTLS_A_UNKNOWN_PSK_IDENTITY); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + return GNUTLS_E_ILLEGAL_SRP_USERNAME; + } + } +#endif + + if (again == 0) + { + datalen = 2 + session_id_len + 1 + TLS_RANDOM_SIZE + 3; + extdatalen = + _gnutls_gen_extensions (session, extdata, sizeof (extdata)); + + if (extdatalen < 0) + { + gnutls_assert (); + return extdatalen; + } + + data = gnutls_alloca (datalen + extdatalen); + if (data == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + data[pos++] = + _gnutls_version_get_major (session->security_parameters.version); + data[pos++] = + _gnutls_version_get_minor (session->security_parameters.version); + + memcpy (&data[pos], + session->security_parameters.server_random, TLS_RANDOM_SIZE); + pos += TLS_RANDOM_SIZE; + + data[pos++] = session_id_len; + if (session_id_len > 0) + { + memcpy (&data[pos], SessionID, session_id_len); + } + pos += session_id_len; + + _gnutls_handshake_log ("HSK[%x]: SessionID: %s\n", session, + _gnutls_bin2hex (SessionID, session_id_len, + buf, sizeof (buf))); + + memcpy (&data[pos], + session->security_parameters.current_cipher_suite.suite, 2); + pos += 2; + + comp = + (uint8_t) _gnutls_compression_get_num (session-> + internals.compression_method); + data[pos++] = comp; + + + if (extdatalen > 0) + { + datalen += extdatalen; + + memcpy (&data[pos], extdata, extdatalen); + } + } + + ret = + _gnutls_send_handshake (session, data, datalen, + GNUTLS_HANDSHAKE_SERVER_HELLO); + gnutls_afree (data); + + return ret; +} + +int +_gnutls_send_hello (gnutls_session_t session, int again) +{ + int ret; + + if (session->security_parameters.entity == GNUTLS_CLIENT) + { + ret = _gnutls_send_client_hello (session, again); + + } + else + { /* SERVER */ + ret = _gnutls_send_server_hello (session, again); + } + + return ret; +} + +/* RECEIVE A HELLO MESSAGE. This should be called from gnutls_recv_handshake_int only if a + * hello message is expected. It uses the security_parameters.current_cipher_suite + * and internals.compression_method. + */ +int +_gnutls_recv_hello (gnutls_session_t session, opaque * data, int datalen) +{ + int ret; + + if (session->security_parameters.entity == GNUTLS_CLIENT) + { + ret = _gnutls_read_server_hello (session, data, datalen); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + } + else + { /* Server side reading a client hello */ + + ret = _gnutls_read_client_hello (session, data, datalen); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + } + + return ret; +} + +/* The packets in gnutls_handshake (it's more broad than original TLS handshake) + * + * Client Server + * + * ClientHello --------> + * <-------- ServerHello + * + * Certificate* + * ServerKeyExchange* + * <-------- CertificateRequest* + * + * <-------- ServerHelloDone + * Certificate* + * ClientKeyExchange + * CertificateVerify* + * [ChangeCipherSpec] + * Finished --------> + * [ChangeCipherSpec] + * <-------- Finished + * + * (*): means optional packet. + */ + +/** + * gnutls_rehandshake - This function will renegotiate security parameters + * @session: is a #gnutls_session_t structure. + * + * This function will renegotiate security parameters with the + * client. This should only be called in case of a server. + * + * This message informs the peer that we want to renegotiate + * parameters (perform a handshake). + * + * If this function succeeds (returns 0), you must call the + * gnutls_handshake() function in order to negotiate the new + * parameters. + * + * If the client does not wish to renegotiate parameters he will + * should with an alert message, thus the return code will be + * %GNUTLS_E_WARNING_ALERT_RECEIVED and the alert will be + * %GNUTLS_A_NO_RENEGOTIATION. A client may also choose to ignore + * this message. + * + * Returns: %GNUTLS_E_SUCCESS on success, otherwise an error. + * + **/ +int +gnutls_rehandshake (gnutls_session_t session) +{ + int ret; + + /* only server sends that handshake packet */ + if (session->security_parameters.entity == GNUTLS_CLIENT) + return GNUTLS_E_INVALID_REQUEST; + + ret = + _gnutls_send_empty_handshake (session, GNUTLS_HANDSHAKE_HELLO_REQUEST, + AGAIN (STATE50)); + STATE = STATE50; + + if (ret < 0) + { + gnutls_assert (); + return ret; + } + STATE = STATE0; + + return 0; +} + +inline static int +_gnutls_abort_handshake (gnutls_session_t session, int ret) +{ + if (((ret == GNUTLS_E_WARNING_ALERT_RECEIVED) && + (gnutls_alert_get (session) == GNUTLS_A_NO_RENEGOTIATION)) + || ret == GNUTLS_E_GOT_APPLICATION_DATA) + return 0; + + /* this doesn't matter */ + return GNUTLS_E_INTERNAL_ERROR; +} + + +/* This function initialized the handshake hash session. + * required for finished messages. + */ +inline static int +_gnutls_handshake_hash_init (gnutls_session_t session) +{ + + if (session->internals.handshake_mac_handle_md5 == NULL) + { + session->internals.handshake_mac_handle_md5 = + _gnutls_hash_init (GNUTLS_MAC_MD5); + + if (session->internals.handshake_mac_handle_md5 == GNUTLS_HASH_FAILED) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + } + + if (session->internals.handshake_mac_handle_sha == NULL) + { + session->internals.handshake_mac_handle_sha = + _gnutls_hash_init (GNUTLS_MAC_SHA1); + if (session->internals.handshake_mac_handle_sha == GNUTLS_HASH_FAILED) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + } + + return 0; +} + +int +_gnutls_send_supplemental (gnutls_session_t session, int again) +{ + int ret = 0; + + _gnutls_debug_log ("EXT[%x]: Sending supplemental data\n", session); + + if (again) + ret = _gnutls_send_handshake (session, NULL, 0, + GNUTLS_HANDSHAKE_SUPPLEMENTAL); + else + { + gnutls_buffer buf; + _gnutls_buffer_init (&buf); + + ret = _gnutls_gen_supplemental (session, &buf); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + ret = _gnutls_send_handshake (session, buf.data, buf.length, + GNUTLS_HANDSHAKE_SUPPLEMENTAL); + _gnutls_buffer_clear (&buf); + } + + return ret; +} + +int +_gnutls_recv_supplemental (gnutls_session_t session) +{ + uint8_t *data = NULL; + int datalen = 0; + int ret; + + _gnutls_debug_log ("EXT[%x]: Expecting supplemental data\n", session); + + ret = _gnutls_recv_handshake (session, &data, &datalen, + GNUTLS_HANDSHAKE_SUPPLEMENTAL, + OPTIONAL_PACKET); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + ret = _gnutls_parse_supplemental (session, data, datalen); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + gnutls_free (data); + + return ret; +} + +/** + * gnutls_handshake - This is the main function in the handshake protocol. + * @session: is a #gnutls_session_t structure. + * + * This function does the handshake of the TLS/SSL protocol, and + * initializes the TLS connection. + * + * This function will fail if any problem is encountered, and will + * return a negative error code. In case of a client, if the client + * has asked to resume a session, but the server couldn't, then a + * full handshake will be performed. + * + * The non-fatal errors such as %GNUTLS_E_AGAIN and + * %GNUTLS_E_INTERRUPTED interrupt the handshake procedure, which + * should be later be resumed. Call this function again, until it + * returns 0; cf. gnutls_record_get_direction() and + * gnutls_error_is_fatal(). + * + * If this function is called by a server after a rehandshake request + * then %GNUTLS_E_GOT_APPLICATION_DATA or + * %GNUTLS_E_WARNING_ALERT_RECEIVED may be returned. Note that these + * are non fatal errors, only in the specific case of a rehandshake. + * Their meaning is that the client rejected the rehandshake request. + * + * Returns: %GNUTLS_E_SUCCESS on success, otherwise an error. + * + **/ +int +gnutls_handshake (gnutls_session_t session) +{ + int ret; + + if ((ret = _gnutls_handshake_hash_init (session)) < 0) + { + gnutls_assert (); + return ret; + } + + if (session->security_parameters.entity == GNUTLS_CLIENT) + { + ret = _gnutls_handshake_client (session); + } + else + { + ret = _gnutls_handshake_server (session); + } + if (ret < 0) + { + /* In the case of a rehandshake abort + * we should reset the handshake's internal state. + */ + if (_gnutls_abort_handshake (session, ret) == 0) + STATE = STATE0; + + return ret; + } + + ret = _gnutls_handshake_common (session); + + if (ret < 0) + { + if (_gnutls_abort_handshake (session, ret) == 0) + STATE = STATE0; + + return ret; + } + + STATE = STATE0; + + _gnutls_handshake_io_buffer_clear (session); + _gnutls_handshake_internal_state_clear (session); + + return 0; +} + +#define IMED_RET( str, ret) do { \ + if (ret < 0) { \ + if (gnutls_error_is_fatal(ret)==0) return ret; \ + gnutls_assert(); \ + ERR( str, ret); \ + _gnutls_handshake_hash_buffers_clear(session); \ + return ret; \ + } } while (0) + + + +/* + * _gnutls_handshake_client + * This function performs the client side of the handshake of the TLS/SSL protocol. + */ +int +_gnutls_handshake_client (gnutls_session_t session) +{ + int ret = 0; + +#ifdef HANDSHAKE_DEBUG + char buf[64]; + + if (session->internals.resumed_security_parameters.session_id_size > 0) + _gnutls_handshake_log ("HSK[%x]: Ask to resume: %s\n", session, + _gnutls_bin2hex (session->internals. + resumed_security_parameters. + session_id, + session->internals. + resumed_security_parameters. + session_id_size, buf, + sizeof (buf))); +#endif + + switch (STATE) + { + case STATE0: + case STATE1: + ret = _gnutls_send_hello (session, AGAIN (STATE1)); + STATE = STATE1; + IMED_RET ("send hello", ret); + + case STATE2: + /* receive the server hello */ + ret = + _gnutls_recv_handshake (session, NULL, NULL, + GNUTLS_HANDSHAKE_SERVER_HELLO, + MANDATORY_PACKET); + STATE = STATE2; + IMED_RET ("recv hello", ret); + + case STATE70: + if (session->security_parameters.extensions.do_recv_supplemental) + { + ret = _gnutls_recv_supplemental (session); + STATE = STATE70; + IMED_RET ("recv supplemental", ret); + } + + case STATE3: + /* RECV CERTIFICATE */ + if (session->internals.resumed == RESUME_FALSE) /* if we are not resuming */ + ret = _gnutls_recv_server_certificate (session); + STATE = STATE3; + IMED_RET ("recv server certificate", ret); + + case STATE4: + /* receive the server key exchange */ + if (session->internals.resumed == RESUME_FALSE) /* if we are not resuming */ + ret = _gnutls_recv_server_kx_message (session); + STATE = STATE4; + IMED_RET ("recv server kx message", ret); + + case STATE5: + /* receive the server certificate request - if any + */ + + if (session->internals.resumed == RESUME_FALSE) /* if we are not resuming */ + ret = _gnutls_recv_server_certificate_request (session); + STATE = STATE5; + IMED_RET ("recv server certificate request message", ret); + + case STATE6: + /* receive the server hello done */ + if (session->internals.resumed == RESUME_FALSE) /* if we are not resuming */ + ret = + _gnutls_recv_handshake (session, NULL, NULL, + GNUTLS_HANDSHAKE_SERVER_HELLO_DONE, + MANDATORY_PACKET); + STATE = STATE6; + IMED_RET ("recv server hello done", ret); + + case STATE71: + if (session->security_parameters.extensions.do_send_supplemental) + { + ret = _gnutls_send_supplemental (session, AGAIN (STATE71)); + STATE = STATE71; + IMED_RET ("send supplemental", ret); + } + + case STATE7: + /* send our certificate - if any and if requested + */ + if (session->internals.resumed == RESUME_FALSE) /* if we are not resuming */ + ret = _gnutls_send_client_certificate (session, AGAIN (STATE7)); + STATE = STATE7; + IMED_RET ("send client certificate", ret); + + case STATE8: + if (session->internals.resumed == RESUME_FALSE) /* if we are not resuming */ + ret = _gnutls_send_client_kx_message (session, AGAIN (STATE8)); + STATE = STATE8; + IMED_RET ("send client kx", ret); + + case STATE9: + /* send client certificate verify */ + if (session->internals.resumed == RESUME_FALSE) /* if we are not resuming */ + ret = + _gnutls_send_client_certificate_verify (session, AGAIN (STATE9)); + STATE = STATE9; + IMED_RET ("send client certificate verify", ret); + + STATE = STATE0; + default: + break; + } + + + return 0; +} + +/* This function sends the final handshake packets and initializes connection + */ +static int +_gnutls_send_handshake_final (gnutls_session_t session, int init) +{ + int ret = 0; + + /* Send the CHANGE CIPHER SPEC PACKET */ + + switch (STATE) + { + case STATE0: + case STATE20: + ret = _gnutls_send_change_cipher_spec (session, AGAIN (STATE20)); + STATE = STATE20; + if (ret < 0) + { + ERR ("send ChangeCipherSpec", ret); + gnutls_assert (); + return ret; + } + + /* Initialize the connection session (start encryption) - in case of client + */ + if (init == TRUE) + { + ret = _gnutls_connection_state_init (session); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + } + + ret = _gnutls_write_connection_state_init (session); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + case STATE21: + /* send the finished message */ + ret = _gnutls_send_finished (session, AGAIN (STATE21)); + STATE = STATE21; + if (ret < 0) + { + ERR ("send Finished", ret); + gnutls_assert (); + return ret; + } + + STATE = STATE0; + default: + break; + } + + return 0; +} + +/* This function receives the final handshake packets + * And executes the appropriate function to initialize the + * read session. + */ +static int +_gnutls_recv_handshake_final (gnutls_session_t session, int init) +{ + int ret = 0; + uint8_t ch; + + switch (STATE) + { + case STATE0: + case STATE30: + ret = _gnutls_recv_int (session, GNUTLS_CHANGE_CIPHER_SPEC, -1, &ch, 1); + STATE = STATE30; + if (ret <= 0) + { + ERR ("recv ChangeCipherSpec", ret); + gnutls_assert (); + return (ret < 0) ? ret : GNUTLS_E_UNEXPECTED_PACKET_LENGTH; + } + + /* Initialize the connection session (start encryption) - in case of server */ + if (init == TRUE) + { + ret = _gnutls_connection_state_init (session); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + } + + ret = _gnutls_read_connection_state_init (session); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + case STATE31: + ret = _gnutls_recv_finished (session); + STATE = STATE31; + if (ret < 0) + { + ERR ("recv finished", ret); + gnutls_assert (); + return ret; + } + STATE = STATE0; + default: + break; + } + + + return 0; +} + + /* + * _gnutls_handshake_server + * This function does the server stuff of the handshake protocol. + */ + +int +_gnutls_handshake_server (gnutls_session_t session) +{ + int ret = 0; + + switch (STATE) + { + case STATE0: + case STATE1: + ret = + _gnutls_recv_handshake (session, NULL, NULL, + GNUTLS_HANDSHAKE_CLIENT_HELLO, + MANDATORY_PACKET); + STATE = STATE1; + IMED_RET ("recv hello", ret); + + case STATE2: + ret = _gnutls_send_hello (session, AGAIN (STATE2)); + STATE = STATE2; + IMED_RET ("send hello", ret); + + case STATE70: + if (session->security_parameters.extensions.do_send_supplemental) + { + ret = _gnutls_send_supplemental (session, AGAIN (STATE70)); + STATE = STATE70; + IMED_RET ("send supplemental data", ret); + } + + /* SEND CERTIFICATE + KEYEXCHANGE + CERTIFICATE_REQUEST */ + case STATE3: + /* NOTE: these should not be send if we are resuming */ + + if (session->internals.resumed == RESUME_FALSE) + ret = _gnutls_send_server_certificate (session, AGAIN (STATE3)); + STATE = STATE3; + IMED_RET ("send server certificate", ret); + + case STATE4: + /* send server key exchange (A) */ + if (session->internals.resumed == RESUME_FALSE) + ret = _gnutls_send_server_kx_message (session, AGAIN (STATE4)); + STATE = STATE4; + IMED_RET ("send server kx", ret); + + case STATE5: + /* Send certificate request - if requested to */ + if (session->internals.resumed == RESUME_FALSE) + ret = + _gnutls_send_server_certificate_request (session, AGAIN (STATE5)); + STATE = STATE5; + IMED_RET ("send server cert request", ret); + + case STATE6: + /* send the server hello done */ + if (session->internals.resumed == RESUME_FALSE) /* if we are not resuming */ + ret = + _gnutls_send_empty_handshake (session, + GNUTLS_HANDSHAKE_SERVER_HELLO_DONE, + AGAIN (STATE6)); + STATE = STATE6; + IMED_RET ("send server hello done", ret); + + case STATE71: + if (session->security_parameters.extensions.do_recv_supplemental) + { + ret = _gnutls_recv_supplemental (session); + STATE = STATE71; + IMED_RET ("recv client supplemental", ret); + } + + /* RECV CERTIFICATE + KEYEXCHANGE + CERTIFICATE_VERIFY */ + case STATE7: + /* receive the client certificate message */ + if (session->internals.resumed == RESUME_FALSE) /* if we are not resuming */ + ret = _gnutls_recv_client_certificate (session); + STATE = STATE7; + IMED_RET ("recv client certificate", ret); + + case STATE8: + /* receive the client key exchange message */ + if (session->internals.resumed == RESUME_FALSE) /* if we are not resuming */ + ret = _gnutls_recv_client_kx_message (session); + STATE = STATE8; + IMED_RET ("recv client kx", ret); + + case STATE9: + /* receive the client certificate verify message */ + if (session->internals.resumed == RESUME_FALSE) /* if we are not resuming */ + ret = _gnutls_recv_client_certificate_verify_message (session); + STATE = STATE9; + IMED_RET ("recv client certificate verify", ret); + + STATE = STATE0; /* finished thus clear session */ + default: + break; + } + + return 0; +} + +int +_gnutls_handshake_common (gnutls_session_t session) +{ + int ret = 0; + + /* send and recv the change cipher spec and finished messages */ + if ((session->internals.resumed == RESUME_TRUE + && session->security_parameters.entity == GNUTLS_CLIENT) + || (session->internals.resumed == RESUME_FALSE + && session->security_parameters.entity == GNUTLS_SERVER)) + { + /* if we are a client resuming - or we are a server not resuming */ + + ret = _gnutls_recv_handshake_final (session, TRUE); + IMED_RET ("recv handshake final", ret); + + ret = _gnutls_send_handshake_final (session, FALSE); + IMED_RET ("send handshake final", ret); + } + else + { /* if we are a client not resuming - or we are a server resuming */ + + ret = _gnutls_send_handshake_final (session, TRUE); + IMED_RET ("send handshake final 2", ret); + + ret = _gnutls_recv_handshake_final (session, FALSE); + IMED_RET ("recv handshake final 2", ret); + } + + if (session->security_parameters.entity == GNUTLS_SERVER) + { + /* in order to support session resuming */ + _gnutls_server_register_current_session (session); + } + + /* clear handshake buffer */ + _gnutls_handshake_hash_buffers_clear (session); + return ret; + +} + +int +_gnutls_generate_session_id (opaque * session_id, uint8_t * len) +{ + *len = TLS_MAX_SESSION_ID_SIZE; + + if (gc_nonce (session_id, *len) != GC_OK) + { + gnutls_assert (); + return GNUTLS_E_RANDOM_FAILED; + } + + return 0; +} + +int +_gnutls_recv_hello_request (gnutls_session_t session, void *data, + uint32_t data_size) +{ + uint8_t type; + + if (session->security_parameters.entity == GNUTLS_SERVER) + { + gnutls_assert (); + return GNUTLS_E_UNEXPECTED_PACKET; + } + if (data_size < 1) + { + gnutls_assert (); + return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; + } + type = ((uint8_t *) data)[0]; + if (type == GNUTLS_HANDSHAKE_HELLO_REQUEST) + return GNUTLS_E_REHANDSHAKE; + else + { + gnutls_assert (); + return GNUTLS_E_UNEXPECTED_PACKET; + } +} + +/* Returns 1 if the given KX has not the corresponding parameters + * (DH or RSA) set up. Otherwise returns 0. + */ +inline static int +check_server_params (gnutls_session_t session, + gnutls_kx_algorithm_t kx, + gnutls_kx_algorithm_t * alg, int alg_size) +{ + int cred_type; + gnutls_dh_params_t dh_params = NULL; + gnutls_rsa_params_t rsa_params = NULL; + int j; + + cred_type = _gnutls_map_kx_get_cred (kx, 1); + + /* Read the Diffie Hellman parameters, if any. + */ + if (cred_type == GNUTLS_CRD_CERTIFICATE) + { + int delete; + gnutls_certificate_credentials_t x509_cred = + (gnutls_certificate_credentials_t) _gnutls_get_cred (session->key, + cred_type, NULL); + + if (x509_cred != NULL) + { + dh_params = + _gnutls_get_dh_params (x509_cred->dh_params, + x509_cred->params_func, session); + rsa_params = + _gnutls_certificate_get_rsa_params (x509_cred->rsa_params, + x509_cred->params_func, + session); + } + + /* Check also if the certificate supports the + * KX method. + */ + delete = 1; + for (j = 0; j < alg_size; j++) + { + if (alg[j] == kx) + { + delete = 0; + break; + } + } + + if (delete == 1) + return 1; + +#ifdef ENABLE_ANON + } + else if (cred_type == GNUTLS_CRD_ANON) + { + gnutls_anon_server_credentials_t anon_cred = + (gnutls_anon_server_credentials_t) _gnutls_get_cred (session->key, + cred_type, NULL); + + if (anon_cred != NULL) + { + dh_params = + _gnutls_get_dh_params (anon_cred->dh_params, + anon_cred->params_func, session); + } +#endif +#ifdef ENABLE_PSK + } + else if (cred_type == GNUTLS_CRD_PSK) + { + gnutls_psk_server_credentials_t psk_cred = + (gnutls_psk_server_credentials_t) _gnutls_get_cred (session->key, + cred_type, NULL); + + if (psk_cred != NULL) + { + dh_params = + _gnutls_get_dh_params (psk_cred->dh_params, psk_cred->params_func, + session); + } +#endif + } + else + return 0; /* no need for params */ + + + /* If the key exchange method needs RSA or DH params, + * but they are not set then remove it. + */ + if (_gnutls_kx_needs_rsa_params (kx) != 0) + { + /* needs rsa params. */ + if (_gnutls_rsa_params_to_mpi (rsa_params) == NULL) + { + gnutls_assert (); + return 1; + } + } + + if (_gnutls_kx_needs_dh_params (kx) != 0) + { + /* needs DH params. */ + if (_gnutls_dh_params_to_mpi (dh_params) == NULL) + { + gnutls_assert (); + return 1; + } + } + + return 0; +} + +/* This function will remove algorithms that are not supported by + * the requested authentication method. We remove an algorithm if + * we have a certificate with keyUsage bits set. + * + * This does a more high level check than gnutls_supported_ciphersuites(), + * by checking certificates etc. + */ +int +_gnutls_remove_unwanted_ciphersuites (gnutls_session_t session, + cipher_suite_st ** cipherSuites, + int numCipherSuites, + gnutls_pk_algorithm_t requested_pk_algo) +{ + + int ret = 0; + cipher_suite_st *newSuite, cs; + int newSuiteSize = 0, i; + gnutls_certificate_credentials_t cert_cred; + gnutls_kx_algorithm_t kx; + int server = session->security_parameters.entity == GNUTLS_SERVER ? 1 : 0; + gnutls_kx_algorithm_t *alg = NULL; + int alg_size = 0; + + /* if we should use a specific certificate, + * we should remove all algorithms that are not supported + * by that certificate and are on the same authentication + * method (CERTIFICATE). + */ + + cert_cred = + (gnutls_certificate_credentials_t) _gnutls_get_cred (session->key, + GNUTLS_CRD_CERTIFICATE, + NULL); + + /* If there are certificate credentials, find an appropriate certificate + * or disable them; + */ + if (session->security_parameters.entity == GNUTLS_SERVER + && cert_cred != NULL) + { + ret = _gnutls_server_select_cert (session, requested_pk_algo); + if (ret < 0) + { + gnutls_assert (); + _gnutls_x509_log ("Could not find an appropriate certificate: %s\n", + gnutls_strerror (ret)); + cert_cred = NULL; + } + } + + /* get all the key exchange algorithms that are + * supported by the X509 certificate parameters. + */ + if ((ret = + _gnutls_selected_cert_supported_kx (session, &alg, &alg_size)) < 0) + { + gnutls_assert (); + return ret; + } + + newSuite = gnutls_malloc (numCipherSuites * sizeof (cipher_suite_st)); + if (newSuite == NULL) + { + gnutls_assert (); + gnutls_free (alg); + return GNUTLS_E_MEMORY_ERROR; + } + + /* now removes ciphersuites based on the KX algorithm + */ + for (i = 0; i < numCipherSuites; i++) + { + int delete = 0; + + /* finds the key exchange algorithm in + * the ciphersuite + */ + kx = _gnutls_cipher_suite_get_kx_algo (&(*cipherSuites)[i]); + + /* if it is defined but had no credentials + */ + if (_gnutls_get_kx_cred (session, kx, NULL) == NULL) + { + delete = 1; + } + else + { + delete = 0; + + if (server) + delete = check_server_params (session, kx, alg, alg_size); + } + + /* These two SRP kx's are marked to require a CRD_CERTIFICATE, + (see cred_mappings in gnutls_algorithms.c), but it also + requires a SRP credential. Don't use SRP kx unless we have a + SRP credential too. */ + if (kx == GNUTLS_KX_SRP_RSA || kx == GNUTLS_KX_SRP_DSS) + { + if (!_gnutls_get_cred (session->key, GNUTLS_CRD_SRP, NULL)) + delete = 1; + } + + memcpy (&cs.suite, &(*cipherSuites)[i].suite, 2); + + if (delete == 0) + { + + _gnutls_handshake_log ("HSK[%x]: Keeping ciphersuite: %s\n", + session, + _gnutls_cipher_suite_get_name (&cs)); + + memcpy (newSuite[newSuiteSize].suite, (*cipherSuites)[i].suite, 2); + newSuiteSize++; + } + else + { + _gnutls_handshake_log ("HSK[%x]: Removing ciphersuite: %s\n", + session, + _gnutls_cipher_suite_get_name (&cs)); + + } + } + + gnutls_free (alg); + gnutls_free (*cipherSuites); + *cipherSuites = newSuite; + + ret = newSuiteSize; + + return ret; + +} + +/** + * gnutls_handshake_set_max_packet_length - This function will set the maximum length of a handshake message + * @session: is a #gnutls_session_t structure. + * @max: is the maximum number. + * + * This function will set the maximum size of a handshake message. + * Handshake messages over this size are rejected. The default value + * is 16kb which is large enough. Set this to 0 if you do not want to + * set an upper limit. + * + **/ +void +gnutls_handshake_set_max_packet_length (gnutls_session_t session, size_t max) +{ + session->internals.max_handshake_data_buffer_size = max; +} + +void +_gnutls_set_adv_version (gnutls_session_t session, gnutls_protocol_t ver) +{ + set_adv_version (session, _gnutls_version_get_major (ver), + _gnutls_version_get_minor (ver)); +} + +gnutls_protocol_t +_gnutls_get_adv_version (gnutls_session_t session) +{ + return _gnutls_version_get (_gnutls_get_adv_version_major (session), + _gnutls_get_adv_version_minor (session)); +} + +/** + * gnutls_handshake_get_last_in - Returns the last handshake message received. + * @session: is a #gnutls_session_t structure. + * + * This function is only useful to check where the last performed + * handshake failed. If the previous handshake succeed or was not + * performed at all then no meaningful value will be returned. + * + * Check %gnutls_handshake_description_t in gnutls.h for the + * available handshake descriptions. + * + * Returns: the last handshake message type received, a + * %gnutls_handshake_description_t. + **/ +gnutls_handshake_description_t +gnutls_handshake_get_last_in (gnutls_session_t session) +{ + return session->internals.last_handshake_in; +} + +/** + * gnutls_handshake_get_last_out - Returns the last handshake message sent. + * @session: is a #gnutls_session_t structure. + * + * This function is only useful to check where the last performed + * handshake failed. If the previous handshake succeed or was not + * performed at all then no meaningful value will be returned. + * + * Check %gnutls_handshake_description_t in gnutls.h for the + * available handshake descriptions. + * + * Returns: the last handshake message type sent, a + * %gnutls_handshake_description_t. + **/ +gnutls_handshake_description_t +gnutls_handshake_get_last_out (gnutls_session_t session) +{ + return session->internals.last_handshake_out; +} diff --git a/src/daemon/https/tls/gnutls_handshake.h b/src/daemon/https/tls/gnutls_handshake.h new file mode 100644 index 00000000..5cff279a --- /dev/null +++ b/src/daemon/https/tls/gnutls_handshake.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +typedef enum Optional +{ OPTIONAL_PACKET, MANDATORY_PACKET } Optional; + +int _gnutls_send_handshake (gnutls_session_t session, void *i_data, + uint32_t i_datasize, + gnutls_handshake_description_t type); +int _gnutls_recv_hello_request (gnutls_session_t session, void *data, + uint32_t data_size); +int _gnutls_send_hello (gnutls_session_t session, int again); +int _gnutls_recv_hello (gnutls_session_t session, opaque * data, int datalen); +int _gnutls_recv_handshake (gnutls_session_t session, uint8_t **, int *, + gnutls_handshake_description_t, + Optional optional); +int _gnutls_generate_session_id (opaque * session_id, uint8_t * len); +int _gnutls_handshake_common (gnutls_session_t session); +int _gnutls_handshake_client (gnutls_session_t session); +int _gnutls_handshake_server (gnutls_session_t session); +void _gnutls_set_server_random (gnutls_session_t session, uint8_t * rnd); +void _gnutls_set_client_random (gnutls_session_t session, uint8_t * rnd); +int _gnutls_tls_create_random (opaque * dst); +int _gnutls_remove_unwanted_ciphersuites (gnutls_session_t session, + cipher_suite_st ** cipherSuites, + int numCipherSuites, + gnutls_pk_algorithm_t); +int _gnutls_find_pk_algos_in_ciphersuites (opaque * data, int datalen); +int _gnutls_server_select_suite (gnutls_session_t session, opaque * data, + int datalen); + +int _gnutls_negotiate_version( gnutls_session_t session, gnutls_protocol_t adv_version); +int _gnutls_user_hello_func( gnutls_session, gnutls_protocol_t adv_version); + +#define STATE session->internals.handshake_state +/* This returns true if we have got there + * before (and not finished due to an interrupt). + */ +#define AGAIN(target) STATE==target?1:0 diff --git a/src/daemon/https/tls/gnutls_hash_int.c b/src/daemon/https/tls/gnutls_hash_int.c new file mode 100644 index 00000000..2574f213 --- /dev/null +++ b/src/daemon/https/tls/gnutls_hash_int.c @@ -0,0 +1,446 @@ +/* + * Copyright (C) 2000, 2001, 2004, 2005, 2007 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +/* This file handles all the internal functions that cope with hashes + * and HMACs. + */ + +#include +#include +#include + +static inline Gc_hash +_gnutls_mac2gc (gnutls_mac_algorithm_t mac) +{ + switch (mac) + { + case GNUTLS_MAC_NULL: + return -1; + break; + case GNUTLS_MAC_SHA1: + return GC_SHA1; + break; + case GNUTLS_MAC_SHA256: + return GC_SHA256; + break; + case GNUTLS_MAC_MD5: + return GC_MD5; + break; + default: + gnutls_assert (); + return -1; + } + return -1; +} + +GNUTLS_HASH_HANDLE +_gnutls_hash_init (gnutls_mac_algorithm_t algorithm) +{ + mac_hd_t ret; + int result; + + ret = gnutls_malloc (sizeof (mac_hd_st)); + if (ret == NULL) + { + gnutls_assert (); + return GNUTLS_HASH_FAILED; + } + + ret->algorithm = algorithm; + + result = gc_hash_open (_gnutls_mac2gc (algorithm), 0, &ret->handle); + if (result) + { + gnutls_assert (); + gnutls_free (ret); + ret = GNUTLS_HASH_FAILED; + } + + return ret; +} + +int +_gnutls_hash_get_algo_len (gnutls_mac_algorithm_t algorithm) +{ + int ret; + + ret = gc_hash_digest_length (_gnutls_mac2gc (algorithm)); + + return ret; + +} + +int +_gnutls_hash (GNUTLS_HASH_HANDLE handle, const void *text, size_t textlen) +{ + if (textlen > 0) + gc_hash_write (handle->handle, textlen, text); + return 0; +} + +GNUTLS_HASH_HANDLE +_gnutls_hash_copy (GNUTLS_HASH_HANDLE handle) +{ + GNUTLS_HASH_HANDLE ret; + int result; + + ret = gnutls_malloc (sizeof (mac_hd_st)); + + if (ret == NULL) + return GNUTLS_HASH_FAILED; + + ret->algorithm = handle->algorithm; + ret->key = NULL; /* it's a hash anyway */ + ret->keysize = 0; + + result = gc_hash_clone (handle->handle, &ret->handle); + + if (result) + { + gnutls_free (ret); + return GNUTLS_HASH_FAILED; + } + + return ret; +} + +void +_gnutls_hash_deinit (GNUTLS_HASH_HANDLE handle, void *digest) +{ + const opaque *mac; + int maclen; + + maclen = _gnutls_hash_get_algo_len (handle->algorithm); + + mac = gc_hash_read (handle->handle); + if (digest != NULL) + memcpy (digest, mac, maclen); + + gc_hash_close (handle->handle); + + gnutls_free (handle); +} + + +mac_hd_t +_gnutls_hmac_init (gnutls_mac_algorithm_t algorithm, + const void *key, int keylen) +{ + mac_hd_t ret; + int result; + + ret = gnutls_malloc (sizeof (mac_hd_st)); + if (ret == NULL) + return GNUTLS_MAC_FAILED; + + result = gc_hash_open (_gnutls_mac2gc (algorithm), GC_HMAC, &ret->handle); + if (result) + { + gnutls_free (ret); + return GNUTLS_MAC_FAILED; + } + + gc_hash_hmac_setkey (ret->handle, keylen, key); + + ret->algorithm = algorithm; + ret->key = key; + ret->keysize = keylen; + + return ret; +} + +void +_gnutls_hmac_deinit (mac_hd_t handle, void *digest) +{ + const opaque *mac; + int maclen; + + maclen = _gnutls_hash_get_algo_len (handle->algorithm); + + mac = gc_hash_read (handle->handle); + + if (digest != NULL) + memcpy (digest, mac, maclen); + + gc_hash_close (handle->handle); + + gnutls_free (handle); +} + +inline static int +get_padsize (gnutls_mac_algorithm_t algorithm) +{ + switch (algorithm) + { + case GNUTLS_MAC_MD5: + return 48; + case GNUTLS_MAC_SHA1: + return 40; + default: + return 0; + } +} + +mac_hd_t +_gnutls_mac_init_ssl3 (gnutls_mac_algorithm_t algorithm, void *key, + int keylen) +{ + mac_hd_t ret; + opaque ipad[48]; + int padsize; + + padsize = get_padsize (algorithm); + if (padsize == 0) + { + gnutls_assert (); + return GNUTLS_MAC_FAILED; + } + + memset (ipad, 0x36, padsize); + + ret = _gnutls_hash_init (algorithm); + if (ret != GNUTLS_HASH_FAILED) + { + ret->key = key; + ret->keysize = keylen; + + if (keylen > 0) + _gnutls_hash (ret, key, keylen); + _gnutls_hash (ret, ipad, padsize); + } + + return ret; +} + +void +_gnutls_mac_deinit_ssl3 (mac_hd_t handle, void *digest) +{ + opaque ret[MAX_HASH_SIZE]; + mac_hd_t td; + opaque opad[48]; + int padsize; + int block; + + padsize = get_padsize (handle->algorithm); + if (padsize == 0) + { + gnutls_assert (); + return; + } + + memset (opad, 0x5C, padsize); + + td = _gnutls_hash_init (handle->algorithm); + if (td != GNUTLS_MAC_FAILED) + { + if (handle->keysize > 0) + _gnutls_hash (td, handle->key, handle->keysize); + + _gnutls_hash (td, opad, padsize); + block = _gnutls_hmac_get_algo_len (handle->algorithm); + _gnutls_hash_deinit (handle, ret); /* get the previous hash */ + _gnutls_hash (td, ret, block); + + _gnutls_hash_deinit (td, digest); + } +} + +void +_gnutls_mac_deinit_ssl3_handshake (mac_hd_t handle, + void *digest, opaque * key, + uint32_t key_size) +{ + opaque ret[MAX_HASH_SIZE]; + mac_hd_t td; + opaque opad[48]; + opaque ipad[48]; + int padsize; + int block; + + padsize = get_padsize (handle->algorithm); + if (padsize == 0) + { + gnutls_assert (); + return; + } + + memset (opad, 0x5C, padsize); + memset (ipad, 0x36, padsize); + + td = _gnutls_hash_init (handle->algorithm); + if (td != GNUTLS_HASH_FAILED) + { + if (key_size > 0) + _gnutls_hash (td, key, key_size); + + _gnutls_hash (td, opad, padsize); + block = _gnutls_hmac_get_algo_len (handle->algorithm); + + if (key_size > 0) + _gnutls_hash (handle, key, key_size); + _gnutls_hash (handle, ipad, padsize); + _gnutls_hash_deinit (handle, ret); /* get the previous hash */ + + _gnutls_hash (td, ret, block); + + _gnutls_hash_deinit (td, digest); + } +} + +static int +ssl3_sha (int i, opaque * secret, int secret_len, + opaque * rnd, int rnd_len, void *digest) +{ + int j; + opaque text1[26]; + + GNUTLS_HASH_HANDLE td; + + for (j = 0; j < i + 1; j++) + { + text1[j] = 65 + i; /* A==65 */ + } + + td = _gnutls_hash_init (GNUTLS_MAC_SHA1); + if (td == NULL) + { + gnutls_assert (); + return GNUTLS_E_HASH_FAILED; + } + + _gnutls_hash (td, text1, i + 1); + _gnutls_hash (td, secret, secret_len); + _gnutls_hash (td, rnd, rnd_len); + + _gnutls_hash_deinit (td, digest); + return 0; +} + +static int +ssl3_md5 (int i, opaque * secret, int secret_len, + opaque * rnd, int rnd_len, void *digest) +{ + opaque tmp[MAX_HASH_SIZE]; + mac_hd_t td; + int ret; + + td = _gnutls_hash_init (GNUTLS_MAC_MD5); + if (td == NULL) + { + gnutls_assert (); + return GNUTLS_E_HASH_FAILED; + } + + _gnutls_hash (td, secret, secret_len); + + ret = ssl3_sha (i, secret, secret_len, rnd, rnd_len, tmp); + if (ret < 0) + { + gnutls_assert (); + _gnutls_hash_deinit (td, digest); + return ret; + } + + _gnutls_hash (td, tmp, _gnutls_hash_get_algo_len (GNUTLS_MAC_SHA1)); + + _gnutls_hash_deinit (td, digest); + return 0; +} + +int +_gnutls_ssl3_hash_md5 (void *first, int first_len, + void *second, int second_len, int ret_len, + opaque * ret) +{ + opaque digest[MAX_HASH_SIZE]; + mac_hd_t td; + int block = _gnutls_hash_get_algo_len (GNUTLS_MAC_MD5); + + td = _gnutls_hash_init (GNUTLS_MAC_MD5); + if (td == NULL) + { + gnutls_assert (); + return GNUTLS_E_HASH_FAILED; + } + + _gnutls_hash (td, first, first_len); + _gnutls_hash (td, second, second_len); + + _gnutls_hash_deinit (td, digest); + + if (ret_len > block) + { + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + memcpy (ret, digest, ret_len); + + return 0; + +} + +int +_gnutls_ssl3_generate_random (void *secret, int secret_len, + void *rnd, int rnd_len, + int ret_bytes, opaque * ret) +{ + int i = 0, copy, output_bytes; + opaque digest[MAX_HASH_SIZE]; + int block = _gnutls_hash_get_algo_len (GNUTLS_MAC_MD5); + int result, times; + + output_bytes = 0; + do + { + output_bytes += block; + } + while (output_bytes < ret_bytes); + + times = output_bytes / block; + + for (i = 0; i < times; i++) + { + + result = ssl3_md5 (i, secret, secret_len, rnd, rnd_len, digest); + if (result < 0) + { + gnutls_assert (); + return result; + } + + if ((1 + i) * block < ret_bytes) + { + copy = block; + } + else + { + copy = ret_bytes - (i) * block; + } + + memcpy (&ret[i * block], digest, copy); + } + + return 0; +} diff --git a/src/daemon/https/tls/gnutls_hash_int.h b/src/daemon/https/tls/gnutls_hash_int.h new file mode 100644 index 00000000..3e1b75e6 --- /dev/null +++ b/src/daemon/https/tls/gnutls_hash_int.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +#ifndef GNUTLS_HASH_INT_H +# define GNUTLS_HASH_INT_H + +#include + +/* for message digests */ + +typedef struct +{ + gc_hash_handle handle; + gnutls_mac_algorithm_t algorithm; + const void *key; + int keysize; +} mac_hd_st; +typedef mac_hd_st *mac_hd_t; +typedef mac_hd_t GNUTLS_HASH_HANDLE; + +#define GNUTLS_HASH_FAILED NULL +#define GNUTLS_MAC_FAILED NULL + +mac_hd_t _gnutls_hmac_init (gnutls_mac_algorithm_t algorithm, + const void *key, int keylen); +#define _gnutls_hmac_get_algo_len _gnutls_hash_get_algo_len +#define _gnutls_hmac _gnutls_hash +void _gnutls_hmac_deinit (mac_hd_t handle, void *digest); + +mac_hd_t _gnutls_mac_init_ssl3 (gnutls_mac_algorithm_t algorithm, void *key, + int keylen); +void _gnutls_mac_deinit_ssl3 (mac_hd_t handle, void *digest); + +GNUTLS_HASH_HANDLE _gnutls_hash_init (gnutls_mac_algorithm_t algorithm); +int _gnutls_hash_get_algo_len (gnutls_mac_algorithm_t algorithm); +int _gnutls_hash (GNUTLS_HASH_HANDLE handle, const void *text, + size_t textlen); +void _gnutls_hash_deinit (GNUTLS_HASH_HANDLE handle, void *digest); + +int _gnutls_ssl3_generate_random (void *secret, int secret_len, + void *rnd, int random_len, int bytes, + opaque * ret); +int _gnutls_ssl3_hash_md5 (void *first, int first_len, void *second, + int second_len, int ret_len, opaque * ret); + +void _gnutls_mac_deinit_ssl3_handshake (mac_hd_t handle, void *digest, + opaque * key, uint32_t key_size); + +GNUTLS_HASH_HANDLE _gnutls_hash_copy (GNUTLS_HASH_HANDLE handle); + +#endif /* GNUTLS_HASH_INT_H */ diff --git a/src/daemon/https/tls/gnutls_int.h b/src/daemon/https/tls/gnutls_int.h new file mode 100644 index 00000000..9c54262e --- /dev/null +++ b/src/daemon/https/tls/gnutls_int.h @@ -0,0 +1,679 @@ +/* + * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +#ifndef GNUTLS_INT_H + +#define GNUTLS_INT_H + +#include + +#include +#include +#include + +/* FIXME: delete this once opencdk has reentrant keyring functions + */ +#define KEYRING_HACK + +#define MAX32 4294967295 +#define MAX24 16777215 +#define MAX16 65535 + +/* The size of a handshake message should not + * be larger than this value. + */ +#define MAX_HANDSHAKE_PACKET_SIZE 48*1024 + +#define TLS_RANDOM_SIZE 32 +#define TLS_MAX_SESSION_ID_SIZE 32 +#define TLS_MASTER_SIZE 48 + +/* The maximum digest size of hash algorithms. + */ +#define MAX_HASH_SIZE 64 + +#define MAX_LOG_SIZE 1024 /* maximum size of log message */ +#define MAX_SRP_USERNAME 128 +#define MAX_SERVER_NAME_SIZE 128 + +/* we can receive up to MAX_EXT_TYPES extensions. + */ +#define MAX_EXT_TYPES 64 + +/* The initial size of the receive + * buffer size. This will grow if larger + * packets are received. + */ +#define INITIAL_RECV_BUFFER_SIZE 256 + +/* the default for TCP */ +#define DEFAULT_LOWAT 1 + +/* expire time for resuming sessions */ +#define DEFAULT_EXPIRE_TIME 3600 + +/* the maximum size of encrypted packets */ +#define DEFAULT_MAX_RECORD_SIZE 16384 +#define RECORD_HEADER_SIZE 5 +#define MAX_RECORD_SEND_SIZE (size_t)session->security_parameters.max_record_send_size +#define MAX_RECORD_RECV_SIZE (size_t)session->security_parameters.max_record_recv_size +#define MAX_PAD_SIZE 255 +#define EXTRA_COMP_SIZE 2048 +#define MAX_RECORD_OVERHEAD MAX_PAD_SIZE+EXTRA_COMP_SIZE +#define MAX_RECV_SIZE MAX_RECORD_OVERHEAD+MAX_RECORD_RECV_SIZE+RECORD_HEADER_SIZE + +#define HANDSHAKE_HEADER_SIZE 4 + +/* defaults for verification functions + */ +#define DEFAULT_VERIFY_DEPTH 32 +#define DEFAULT_VERIFY_BITS 16*1024 + +#define DECR_LEN(len, x) do { len-=x; if (len<0) {gnutls_assert(); return GNUTLS_E_UNEXPECTED_PACKET_LENGTH;} } while (0) +#define DECR_LENGTH_RET(len, x, RET) do { len-=x; if (len<0) {gnutls_assert(); return RET;} } while (0) +#define DECR_LENGTH_COM(len, x, COM) do { len-=x; if (len<0) {gnutls_assert(); COM;} } while (0) + +#define HASH2MAC(x) ((gnutls_mac_algorithm_t)x) + +// TODO rm +/* Additional cast to bring void* to a type castable to int. */ +#define GNUTLS_POINTER_TO_INT_CAST (long) + +#define GNUTLS_POINTER_TO_INT(_) ((int) GNUTLS_POINTER_TO_INT_CAST (_)) +#define GNUTLS_INT_TO_POINTER(_) ((void*) GNUTLS_POINTER_TO_INT_CAST (_)) + +typedef unsigned char opaque; +typedef struct + { + opaque pint[3]; + } uint24; + +#include + +typedef enum change_cipher_spec_t + { + GNUTLS_TYPE_CHANGE_CIPHER_SPEC = 1 + } change_cipher_spec_t; + +typedef enum handshake_state_t + { + STATE0 = 0, STATE1, STATE2, + STATE3, STATE4, STATE5, + STATE6, STATE7, STATE8, STATE9, STATE20 = 20, STATE21, + STATE30 = 30, STATE31, STATE50 = 50, STATE60 = 60, STATE61, STATE62, + STATE70, STATE71 + } handshake_state_t; + +#include + +/* This is the maximum number of algorithms (ciphers or macs etc). + * keep it synced with GNUTLS_MAX_ALGORITHM_NUM in gnutls.h + */ +#define MAX_ALGOS 16 + +#define MAX_CIPHERSUITES 256 + +typedef enum extensions_t + { GNUTLS_EXTENSION_SERVER_NAME = 0, + GNUTLS_EXTENSION_MAX_RECORD_SIZE = 1, + GNUTLS_EXTENSION_CERT_TYPE = 9, +#ifdef ENABLE_OPRFI + GNUTLS_EXTENSION_OPAQUE_PRF_INPUT = ENABLE_OPRFI, +#endif + GNUTLS_EXTENSION_SRP = 12, + GNUTLS_EXTENSION_INNER_APPLICATION = 37703 + } extensions_t; + +typedef enum + { CIPHER_STREAM, CIPHER_BLOCK} cipher_type_t; + +typedef enum valid_session_t + { VALID_TRUE, VALID_FALSE} valid_session_t; +typedef enum resumable_session_t + { RESUME_TRUE, + RESUME_FALSE + } resumable_session_t; + +/* Record Protocol */ +typedef enum content_type_t + { + GNUTLS_CHANGE_CIPHER_SPEC = 20, GNUTLS_ALERT, + GNUTLS_HANDSHAKE, GNUTLS_APPLICATION_DATA, + GNUTLS_INNER_APPLICATION = 24 + } content_type_t; + +#define GNUTLS_PK_ANY (gnutls_pk_algorithm_t)-1 +#define GNUTLS_PK_NONE (gnutls_pk_algorithm_t)-2 + +/* STATE (stop) */ + +typedef void (*LOG_FUNC)(int, + const char *); + +/* Store & Retrieve functions defines: */ +typedef struct auth_cred_st + { + gnutls_credentials_type_t algorithm; + + /* the type of credentials depends on algorithm + */ + void *credentials; + struct auth_cred_st *next; + } auth_cred_st; + +struct gnutls_key_st + { + /* For DH KX */ + gnutls_datum_t key; + mpi_t KEY; + mpi_t client_Y; + mpi_t client_g; + mpi_t client_p; + mpi_t dh_secret; + /* for SRP */ + mpi_t A; + mpi_t B; + mpi_t u; + mpi_t b; + mpi_t a; + mpi_t x; + /* RSA: e, m + */ + mpi_t rsa[2]; + + /* this is used to hold the peers authentication data + */ + /* auth_info_t structures SHOULD NOT contain malloced + * elements. Check gnutls_session_pack.c, and gnutls_auth.c. + * Rememember that this should be calloced! + */ + void *auth_info; + gnutls_credentials_type_t auth_info_type; + int auth_info_size; /* needed in order to store to db for restoring + */ + uint8_t crypt_algo; + + auth_cred_st *cred; /* used to specify keys/certificates etc */ + + int certificate_requested; + /* some ciphersuites use this + * to provide client authentication. + * 1 if client auth was requested + * by the peer, 0 otherwise + *** In case of a server this + * holds 1 if we should wait + * for a client certificate verify + */ + }; +typedef struct gnutls_key_st *gnutls_key_st; + +/* STATE (cont) */ +#include +#include +#include +#include + +typedef struct + { + uint8_t suite[2]; + } cipher_suite_st; + +/* This structure holds parameters got from TLS extension + * mechanism. (some extensions may hold parameters in auth_info_t + * structures also - see SRP). + */ +typedef struct + { + opaque name[MAX_SERVER_NAME_SIZE]; + unsigned name_length; + gnutls_server_name_type_t type; + } server_name_st; + +#define MAX_SERVER_NAME_EXTENSIONS 3 +typedef struct + { + server_name_st server_names[MAX_SERVER_NAME_EXTENSIONS]; + /* limit server_name extensions */ + unsigned server_names_size; + + opaque srp_username[MAX_SRP_USERNAME + 1]; + + /* TLS/IA data. */ + int gnutls_ia_enable, gnutls_ia_peer_enable; + int gnutls_ia_allowskip, gnutls_ia_peer_allowskip; + + /* Used by extensions that enable supplemental data. */ + int do_recv_supplemental, do_send_supplemental; + + /* Opaque PRF input. */ + gnutls_oprfi_callback_func oprfi_cb; + void *oprfi_userdata; + opaque *oprfi_client; + uint16_t oprfi_client_len; + opaque *oprfi_server; + uint16_t oprfi_server_len; + } tls_ext_st; + +/* This flag indicates for an extension whether + * it is useful to application level or TLS level only. + * This is used to parse the application level extensions + * before the user_hello callback is called. + */ +typedef enum tls_ext_parse_type_t + { + EXTENSION_ANY, + EXTENSION_APPLICATION, + EXTENSION_TLS + } tls_ext_parse_type_t; + +/* auth_info_t structures now MAY contain malloced + * elements. + */ + +/* This structure and auth_info_t, are stored in the resume database, + * and are restored, in case of resume. + * Holds all the required parameters to resume the current + * session. + */ + +/* if you add anything in Security_Parameters struct, then + * also modify CPY_COMMON in gnutls_constate.c + */ + +/* Note that the security parameters structure is set up after the + * handshake has finished. The only value you may depend on while + * the handshake is in progress is the cipher suite value. + */ +typedef struct + { + gnutls_connection_end_t entity; + gnutls_kx_algorithm_t kx_algorithm; + /* we've got separate write/read bulk/macs because + * there is a time in handshake where the peer has + * null cipher and we don't + */ + gnutls_cipher_algorithm_t read_bulk_cipher_algorithm; + gnutls_mac_algorithm_t read_mac_algorithm; + gnutls_compression_method_t read_compression_algorithm; + + gnutls_cipher_algorithm_t write_bulk_cipher_algorithm; + gnutls_mac_algorithm_t write_mac_algorithm; + gnutls_compression_method_t write_compression_algorithm; + + /* this is the ciphersuite we are going to use + * moved here from internals in order to be restored + * on resume; + */ + cipher_suite_st current_cipher_suite; + opaque master_secret[TLS_MASTER_SIZE]; + opaque client_random[TLS_RANDOM_SIZE]; + opaque server_random[TLS_RANDOM_SIZE]; + opaque session_id[TLS_MAX_SESSION_ID_SIZE]; + uint8_t session_id_size; + time_t timestamp; + tls_ext_st extensions; + + /* The send size is the one requested by the programmer. + * The recv size is the one negotiated with the peer. + */ + uint16_t max_record_send_size; + uint16_t max_record_recv_size; + /* holds the negotiated certificate type */ + gnutls_certificate_type_t cert_type; + gnutls_protocol_t version; /* moved here */ + /* For TLS/IA. XXX: Move to IA credential? */ + opaque inner_secret[TLS_MASTER_SIZE]; + } security_parameters_st; + +/* This structure holds the generated keys + */ +typedef struct + { + gnutls_datum_t server_write_mac_secret; + gnutls_datum_t client_write_mac_secret; + gnutls_datum_t server_write_IV; + gnutls_datum_t client_write_IV; + gnutls_datum_t server_write_key; + gnutls_datum_t client_write_key; + int generated_keys; /* zero if keys have not + * been generated. Non zero + * otherwise. + */ + } cipher_specs_st; + +typedef struct + { + cipher_hd_t write_cipher_state; + cipher_hd_t read_cipher_state; + comp_hd_t read_compression_state; + comp_hd_t write_compression_state; + gnutls_datum_t read_mac_secret; + gnutls_datum_t write_mac_secret; + uint64 read_sequence_number; + uint64 write_sequence_number; + } conn_stat_st; + +typedef struct + { + unsigned int priority[MAX_ALGOS]; + unsigned int algorithms; + } priority_st; + +/* For the external api */ +struct gnutls_priority_st + { + priority_st cipher; + priority_st mac; + priority_st kx; + priority_st compression; + priority_st protocol; + priority_st cert_type; + + /* to disable record padding */ + int no_padding; + }; + +/* DH and RSA parameters types. + */ +typedef struct gnutls_dh_params_int + { + /* [0] is the prime, [1] is the generator. + */ + mpi_t params[2]; + } dh_params_st; + +typedef struct + { + gnutls_dh_params_t dh_params; + int free_dh_params; + gnutls_rsa_params_t rsa_params; + int free_rsa_params; + } internal_params_st; + +typedef struct + { + opaque header[HANDSHAKE_HEADER_SIZE]; + /* this holds the number of bytes in the handshake_header[] */ + size_t header_size; + /* this holds the length of the handshake packet */ + size_t packet_length; + gnutls_handshake_description_t recv_type; + } handshake_header_buffer_st; + +typedef struct + { + gnutls_buffer application_data_buffer; /* holds data to be delivered to application layer */ + gnutls_buffer handshake_hash_buffer; /* used to keep the last received handshake + * message */ + mac_hd_t handshake_mac_handle_sha; /* hash of the handshake messages */ + mac_hd_t handshake_mac_handle_md5; /* hash of the handshake messages */ + + gnutls_buffer handshake_data_buffer; /* this is a buffer that holds the current handshake message */ + gnutls_buffer ia_data_buffer; /* holds inner application data (TLS/IA) */ + resumable_session_t resumable; /* TRUE or FALSE - if we can resume that session */ + handshake_state_t handshake_state; /* holds + * a number which indicates where + * the handshake procedure has been + * interrupted. If it is 0 then + * no interruption has happened. + */ + + valid_session_t valid_connection; /* true or FALSE - if this session is valid */ + + int may_not_read; /* if it's 0 then we can read/write, otherwise it's forbiden to read/write + */ + int may_not_write; + int read_eof; /* non-zero if we have received a closure alert. */ + + int last_alert; /* last alert received */ + + /* The last handshake messages sent or received. + */ + int last_handshake_in; + int last_handshake_out; + + /* this is the compression method we are going to use */ + gnutls_compression_method_t compression_method; + + /* priorities */ + struct gnutls_priority_st priorities; + + /* resumed session */ + resumable_session_t resumed; /* RESUME_TRUE or FALSE - if we are resuming a session */ + security_parameters_st resumed_security_parameters; + + /* sockets internals */ + int lowat; + + /* These buffers are used in the handshake + * protocol only. freed using _gnutls_handshake_io_buffer_clear(); + */ + gnutls_buffer handshake_send_buffer; + size_t handshake_send_buffer_prev_size; + content_type_t handshake_send_buffer_type; + gnutls_handshake_description_t handshake_send_buffer_htype; + content_type_t handshake_recv_buffer_type; + gnutls_handshake_description_t handshake_recv_buffer_htype; + gnutls_buffer handshake_recv_buffer; + + /* this buffer holds a record packet -mostly used for + * non blocking IO. + */ + gnutls_buffer record_recv_buffer; + gnutls_buffer record_send_buffer; /* holds cached data + * for the gnutls_io_write_buffered() + * function. + */ + size_t record_send_buffer_prev_size; /* holds the + * data written in the previous runs. + */ + size_t record_send_buffer_user_size; /* holds the + * size of the user specified data to + * send. + */ + + /* 0 if no peeked data was kept, 1 otherwise. + */ + int have_peeked_data; + + int expire_time; /* after expire_time seconds this session will expire */ + struct mod_auth_st_int *auth_struct; /* used in handshake packets and KX algorithms */ + int v2_hello; /* 0 if the client hello is v3+. + * non-zero if we got a v2 hello. + */ + /* keeps the headers of the handshake packet + */ + handshake_header_buffer_st handshake_header_buffer; + + /* this is the highest version available + * to the peer. (advertized version). + * This is obtained by the Handshake Client Hello + * message. (some implementations read the Record version) + */ + uint8_t adv_version_major; + uint8_t adv_version_minor; + + /* if this is non zero a certificate request message + * will be sent to the client. - only if the ciphersuite + * supports it. + */ + int send_cert_req; + + /* bits to use for DHE and DHA + * use _gnutls_dh_get_prime_bits() and gnutls_dh_set_prime_bits() + * to access it. + */ + uint16_t dh_prime_bits; + + size_t max_handshake_data_buffer_size; + + /* PUSH & PULL functions. + */ + gnutls_pull_func _gnutls_pull_func; + gnutls_push_func _gnutls_push_func; + /* Holds the first argument of PUSH and PULL + * functions; + */ + gnutls_transport_ptr_t transport_recv_ptr; + gnutls_transport_ptr_t transport_send_ptr; + + /* STORE & RETRIEVE functions. Only used if other + * backend than gdbm is used. + */ + gnutls_db_store_func db_store_func; + gnutls_db_retr_func db_retrieve_func; + gnutls_db_remove_func db_remove_func; + void *db_ptr; + + /* post client hello callback (server side only) + */ + gnutls_handshake_post_client_hello_func user_hello_func; + + /* Holds the record size requested by the + * user. + */ + uint16_t proposed_record_size; + + /* holds the selected certificate and key. + * use _gnutls_selected_certs_deinit() and _gnutls_selected_certs_set() + * to change them. + */ + gnutls_cert *selected_cert_list; + int selected_cert_list_length; + gnutls_privkey *selected_key; + int selected_need_free; + + /* holds the extensions we sent to the peer + * (in case of a client) + */ + uint16_t extensions_sent[MAX_EXT_TYPES]; + uint16_t extensions_sent_size; + + /* is 0 if we are to send the whole PGP key, or non zero + * if the fingerprint is to be sent. + */ + int pgp_fingerprint; + + /* This holds the default version that our first + * record packet will have. */ + opaque default_record_version[2]; + + int cbc_protection_hack; + + void *user_ptr; + + int enable_private; /* non zero to + * enable cipher suites + * which have 0xFF status. + */ + + /* Holds 0 if the last called function was interrupted while + * receiving, and non zero otherwise. + */ + int direction; + + /* This callback will be used (if set) to receive an + * openpgp key. (if the peer sends a fingerprint) + */ + gnutls_openpgp_recv_key_func openpgp_recv_key_func; + + /* If non zero the server will not advertize the CA's he + * trusts (do not send an RDN sequence). + */ + int ignore_rdn_sequence; + + /* This is used to set an arbitary version in the RSA + * PMS secret. Can be used by clients to test whether the + * server checks that version. (** only used in gnutls-cli-debug) + */ + opaque rsa_pms_version[2]; + + char *srp_username; + char *srp_password; + + /* Here we cache the DH or RSA parameters got from the + * credentials structure, or from a callback. That is to + * minimize external calls. + */ + internal_params_st params; + + /* This buffer is used by the record recv functions, + * as a temporary store buffer. + */ + gnutls_datum_t recv_buffer; + + /* To avoid using global variables, and especially on Windows where + * the application may use a different errno variable than GnuTLS, + * it is possible to use gnutls_transport_set_errno to set a + * session-specific errno variable in the user-replaceable push/pull + * functions. This value is used by the send/recv functions. (The + * strange name of this variable is because 'errno' is typically + * #define'd.) + */ + int errnum; + + /* Function used to perform public-key signing operation during + handshake. Used by gnutls_sig.c:_gnutls_tls_sign(), see also + gnutls_sign_callback_set(). */ + gnutls_sign_func sign_func; + void *sign_func_userdata; + + /* If you add anything here, check _gnutls_handshake_internal_state_clear(). + */ + } internals_st; + +struct gnutls_session_int + { + security_parameters_st security_parameters; + cipher_specs_st cipher_specs; + conn_stat_st connection_state; + internals_st internals; + gnutls_key_st key; + }; + +/* functions + */ +void _gnutls_set_current_version(gnutls_session_t session, + gnutls_protocol_t version); + +void _gnutls_free_auth_info(gnutls_session_t session); + +/* These two macros return the advertized TLS version of + * the peer. + */ +#define _gnutls_get_adv_version_major( session) \ + session->internals.adv_version_major + +#define _gnutls_get_adv_version_minor( session) \ + session->internals.adv_version_minor + +#define set_adv_version( session, major, minor) \ + session->internals.adv_version_major = major; \ + session->internals.adv_version_minor = minor + +void _gnutls_set_adv_version(gnutls_session_t, + gnutls_protocol_t); +gnutls_protocol_t _gnutls_get_adv_version(gnutls_session_t); + +#endif /* GNUTLS_INT_H */ diff --git a/src/daemon/https/tls/gnutls_kx.c b/src/daemon/https/tls/gnutls_kx.c new file mode 100644 index 00000000..5e7f0d4d --- /dev/null +++ b/src/daemon/https/tls/gnutls_kx.c @@ -0,0 +1,773 @@ +/* + * Copyright (C) 2000, 2001, 2004, 2005, 2006 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +/* This file contains functions which are wrappers for the key exchange + * part of TLS. They are called by the handshake functions (gnutls_handshake) + */ + +#include "gnutls_int.h" +#include "gnutls_handshake.h" +#include "gnutls_kx.h" +#include "gnutls_dh.h" +#include "gnutls_errors.h" +#include "gnutls_algorithms.h" +#include "debug.h" +#include "gnutls_mpi.h" +#include +#include +#include + +/* This file contains important thing for the TLS handshake procedure. + */ + +#define MASTER_SECRET "master secret" +static int generate_normal_master (gnutls_session_t session, int); + +int +_gnutls_generate_master (gnutls_session_t session, int keep_premaster) +{ + if (session->internals.resumed == RESUME_FALSE) + return generate_normal_master (session, keep_premaster); + return 0; +} + +/* here we generate the TLS Master secret. + */ +#define PREMASTER session->key->key +static int +generate_normal_master (gnutls_session_t session, int keep_premaster) +{ + int ret = 0; + char buf[512]; + + _gnutls_hard_log ("INT: PREMASTER SECRET[%d]: %s\n", PREMASTER.size, + _gnutls_bin2hex (PREMASTER.data, PREMASTER.size, buf, + sizeof (buf))); + _gnutls_hard_log ("INT: CLIENT RANDOM[%d]: %s\n", 32, + _gnutls_bin2hex (session->security_parameters. + client_random, 32, buf, sizeof (buf))); + _gnutls_hard_log ("INT: SERVER RANDOM[%d]: %s\n", 32, + _gnutls_bin2hex (session->security_parameters. + server_random, 32, buf, sizeof (buf))); + + if (gnutls_protocol_get_version (session) == GNUTLS_SSL3) + { + opaque rnd[2 * TLS_RANDOM_SIZE + 1]; + + memcpy (rnd, session->security_parameters.client_random, + TLS_RANDOM_SIZE); + memcpy (&rnd[TLS_RANDOM_SIZE], + session->security_parameters.server_random, TLS_RANDOM_SIZE); + + ret = + _gnutls_ssl3_generate_random (PREMASTER.data, PREMASTER.size, + rnd, 2 * TLS_RANDOM_SIZE, + TLS_MASTER_SIZE, + session->security_parameters. + master_secret); + + } + else if (session->security_parameters.extensions.oprfi_client_len > 0 && + session->security_parameters.extensions.oprfi_server_len > 0) + { + opaque *rnd; + size_t rndlen = 2 * TLS_RANDOM_SIZE; + + rndlen += session->security_parameters.extensions.oprfi_client_len; + rndlen += session->security_parameters.extensions.oprfi_server_len; + + rnd = gnutls_malloc (rndlen + 1); + if (!rnd) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + _gnutls_hard_log ("INT: CLIENT OPRFI[%d]: %s\n", + session->security_parameters. + extensions.oprfi_server_len, + _gnutls_bin2hex (session->security_parameters. + extensions.oprfi_client, + session->security_parameters. + extensions.oprfi_client_len, + buf, sizeof (buf))); + _gnutls_hard_log ("INT: SERVER OPRFI[%d]: %s\n", + session->security_parameters. + extensions.oprfi_server_len, + _gnutls_bin2hex (session->security_parameters. + extensions.oprfi_server, + session->security_parameters. + extensions.oprfi_server_len, + buf, sizeof (buf))); + + memcpy (rnd, session->security_parameters.client_random, + TLS_RANDOM_SIZE); + memcpy (rnd + TLS_RANDOM_SIZE, + session->security_parameters.extensions.oprfi_client, + session->security_parameters.extensions.oprfi_client_len); + memcpy (rnd + TLS_RANDOM_SIZE + + session->security_parameters.extensions.oprfi_client_len, + session->security_parameters.server_random, TLS_RANDOM_SIZE); + memcpy (rnd + TLS_RANDOM_SIZE + + session->security_parameters.extensions.oprfi_client_len + + TLS_RANDOM_SIZE, + session->security_parameters.extensions.oprfi_server, + session->security_parameters.extensions.oprfi_server_len); + + ret = _gnutls_PRF (session, PREMASTER.data, PREMASTER.size, + MASTER_SECRET, strlen (MASTER_SECRET), + rnd, rndlen, TLS_MASTER_SIZE, + session->security_parameters.master_secret); + + gnutls_free (rnd); + } + else + { + opaque rnd[2 * TLS_RANDOM_SIZE + 1]; + + memcpy (rnd, session->security_parameters.client_random, + TLS_RANDOM_SIZE); + memcpy (&rnd[TLS_RANDOM_SIZE], + session->security_parameters.server_random, TLS_RANDOM_SIZE); + + ret = + _gnutls_PRF (session, PREMASTER.data, PREMASTER.size, + MASTER_SECRET, strlen (MASTER_SECRET), + rnd, 2 * TLS_RANDOM_SIZE, TLS_MASTER_SIZE, + session->security_parameters.master_secret); + } + + /* TLS/IA inner secret is derived from the master secret. */ + memcpy (session->security_parameters.inner_secret, + session->security_parameters.master_secret, TLS_MASTER_SIZE); + + if (!keep_premaster) + _gnutls_free_datum (&PREMASTER); + + if (ret < 0) + return ret; + + _gnutls_hard_log ("INT: MASTER SECRET: %s\n", + _gnutls_bin2hex (session->security_parameters. + master_secret, TLS_MASTER_SIZE, buf, + sizeof (buf))); + + return ret; +} + + +/* This is called when we want to receive the key exchange message of the + * server. It does nothing if this type of message is not required + * by the selected ciphersuite. + */ +int +_gnutls_send_server_kx_message (gnutls_session_t session, int again) +{ + uint8_t *data = NULL; + int data_size = 0; + int ret = 0; + + if (session->internals.auth_struct->gnutls_generate_server_kx == NULL) + return 0; + + data = NULL; + data_size = 0; + + if (again == 0) + { + data_size = + session->internals.auth_struct-> + gnutls_generate_server_kx (session, &data); + + if (data_size == GNUTLS_E_INT_RET_0) + { + gnutls_assert (); + return 0; + } + + if (data_size < 0) + { + gnutls_assert (); + return data_size; + } + } + + ret = + _gnutls_send_handshake (session, data, data_size, + GNUTLS_HANDSHAKE_SERVER_KEY_EXCHANGE); + gnutls_free (data); + + if (ret < 0) + { + gnutls_assert (); + return ret; + } + return data_size; +} + +/* This function sends a certificate request message to the + * client. + */ +int +_gnutls_send_server_certificate_request (gnutls_session_t session, int again) +{ + uint8_t *data = NULL; + int data_size = 0; + int ret = 0; + + if (session->internals.auth_struct-> + gnutls_generate_server_certificate_request == NULL) + return 0; + + if (session->internals.send_cert_req <= 0) + return 0; + + data = NULL; + data_size = 0; + + if (again == 0) + { + data_size = + session->internals.auth_struct-> + gnutls_generate_server_certificate_request (session, &data); + + if (data_size < 0) + { + gnutls_assert (); + return data_size; + } + } + ret = + _gnutls_send_handshake (session, data, data_size, + GNUTLS_HANDSHAKE_CERTIFICATE_REQUEST); + gnutls_free (data); + + if (ret < 0) + { + gnutls_assert (); + return ret; + } + return data_size; +} + + +/* This is the function for the client to send the key + * exchange message + */ +int +_gnutls_send_client_kx_message (gnutls_session_t session, int again) +{ + uint8_t *data; + int data_size; + int ret = 0; + + if (session->internals.auth_struct->gnutls_generate_client_kx == NULL) + return 0; + + + data = NULL; + data_size = 0; + + if (again == 0) + { + data_size = + session->internals.auth_struct-> + gnutls_generate_client_kx (session, &data); + if (data_size < 0) + { + gnutls_assert (); + return data_size; + } + } + ret = + _gnutls_send_handshake (session, data, data_size, + GNUTLS_HANDSHAKE_CLIENT_KEY_EXCHANGE); + gnutls_free (data); + + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + return ret; +} + + +/* This is the function for the client to send the certificate + * verify message + */ +int +_gnutls_send_client_certificate_verify (gnutls_session_t session, int again) +{ + uint8_t *data; + int ret = 0; + int data_size; + + /* This is a packet that is only sent by the client + */ + if (session->security_parameters.entity == GNUTLS_SERVER) + return 0; + + /* if certificate verify is not needed just exit + */ + if (session->key->certificate_requested == 0) + return 0; + + if (session->internals.auth_struct->gnutls_generate_client_cert_vrfy == + NULL) + { + gnutls_assert (); + return 0; /* this algorithm does not support cli_cert_vrfy + */ + } + + data = NULL; + data_size = 0; + + if (again == 0) + { + data_size = + session->internals.auth_struct-> + gnutls_generate_client_cert_vrfy (session, &data); + if (data_size < 0) + { + gnutls_assert (); + return data_size; + } + if (data_size == 0) + return 0; + + } + ret = + _gnutls_send_handshake (session, data, + data_size, GNUTLS_HANDSHAKE_CERTIFICATE_VERIFY); + gnutls_free (data); + + return ret; +} + + +int +_gnutls_recv_server_kx_message (gnutls_session_t session) +{ + uint8_t *data = NULL; + int datasize; + int ret = 0; + + if (session->internals.auth_struct->gnutls_process_server_kx != NULL) + { + + /* EXCEPTION FOR RSA_EXPORT cipher suite + */ + if (_gnutls_session_is_export (session) != 0 && + _gnutls_peers_cert_less_512 (session) != 0) + { + gnutls_assert (); + return 0; + } + + ret = + _gnutls_recv_handshake (session, &data, + &datasize, + GNUTLS_HANDSHAKE_SERVER_KEY_EXCHANGE, + MANDATORY_PACKET); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + ret = + session->internals.auth_struct-> + gnutls_process_server_kx (session, data, datasize); + gnutls_free (data); + + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + } + return ret; +} + +int +_gnutls_recv_server_certificate_request (gnutls_session_t session) +{ + uint8_t *data; + int datasize; + int ret = 0; + + if (session->internals.auth_struct-> + gnutls_process_server_certificate_request != NULL) + { + + ret = + _gnutls_recv_handshake (session, &data, + &datasize, + GNUTLS_HANDSHAKE_CERTIFICATE_REQUEST, + OPTIONAL_PACKET); + if (ret < 0) + return ret; + + if (ret == 0 && datasize == 0) + return 0; /* ignored */ + + ret = + session->internals.auth_struct-> + gnutls_process_server_certificate_request (session, data, datasize); + gnutls_free (data); + if (ret < 0) + return ret; + + } + return ret; +} + +int +_gnutls_recv_client_kx_message (gnutls_session_t session) +{ + uint8_t *data; + int datasize; + int ret = 0; + + + /* Do key exchange only if the algorithm permits it */ + if (session->internals.auth_struct->gnutls_process_client_kx != NULL) + { + + ret = + _gnutls_recv_handshake (session, &data, + &datasize, + GNUTLS_HANDSHAKE_CLIENT_KEY_EXCHANGE, + MANDATORY_PACKET); + if (ret < 0) + return ret; + + ret = + session->internals.auth_struct-> + gnutls_process_client_kx (session, data, datasize); + gnutls_free (data); + if (ret < 0) + return ret; + + } + + return ret; +} + + +/* This is called when we want send our certificate + */ +int +_gnutls_send_client_certificate (gnutls_session_t session, int again) +{ + uint8_t *data = NULL; + int data_size = 0; + int ret = 0; + + + if (session->key->certificate_requested == 0) + return 0; + + if (session->internals.auth_struct-> + gnutls_generate_client_certificate == NULL) + return 0; + + data = NULL; + data_size = 0; + + if (again == 0) + { + if (gnutls_protocol_get_version (session) != GNUTLS_SSL3 || + session->internals.selected_cert_list_length > 0) + { + /* TLS 1.0 or SSL 3.0 with a valid certificate + */ + data_size = + session->internals.auth_struct-> + gnutls_generate_client_certificate (session, &data); + + if (data_size < 0) + { + gnutls_assert (); + return data_size; + } + } + } + + /* In the SSL 3.0 protocol we need to send a + * no certificate alert instead of an + * empty certificate. + */ + if (gnutls_protocol_get_version (session) == GNUTLS_SSL3 && + session->internals.selected_cert_list_length == 0) + { + ret = + gnutls_alert_send (session, GNUTLS_AL_WARNING, + GNUTLS_A_SSL3_NO_CERTIFICATE); + + } + else + { /* TLS 1.0 or SSL 3.0 with a valid certificate + */ + ret = + _gnutls_send_handshake (session, data, data_size, + GNUTLS_HANDSHAKE_CERTIFICATE_PKT); + gnutls_free (data); + } + + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + return data_size; +} + + +/* This is called when we want send our certificate + */ +int +_gnutls_send_server_certificate (gnutls_session_t session, int again) +{ + uint8_t *data = NULL; + int data_size = 0; + int ret = 0; + + + if (session->internals.auth_struct-> + gnutls_generate_server_certificate == NULL) + return 0; + + data = NULL; + data_size = 0; + + if (again == 0) + { + data_size = + session->internals.auth_struct-> + gnutls_generate_server_certificate (session, &data); + + if (data_size < 0) + { + gnutls_assert (); + return data_size; + } + } + ret = + _gnutls_send_handshake (session, data, data_size, + GNUTLS_HANDSHAKE_CERTIFICATE_PKT); + gnutls_free (data); + + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + return data_size; +} + + +int +_gnutls_recv_client_certificate (gnutls_session_t session) +{ + int datasize; + opaque *data; + int ret = 0; + int optional; + + if (session->internals.auth_struct-> + gnutls_process_client_certificate != NULL) + { + + /* if we have not requested a certificate then just return + */ + if (session->internals.send_cert_req == 0) + { + return 0; + } + + if (session->internals.send_cert_req == GNUTLS_CERT_REQUIRE) + optional = MANDATORY_PACKET; + else + optional = OPTIONAL_PACKET; + + ret = + _gnutls_recv_handshake (session, &data, + &datasize, + GNUTLS_HANDSHAKE_CERTIFICATE_PKT, optional); + + if (ret < 0) + { + /* Handle the case of old SSL3 clients who send + * a warning alert instead of an empty certificate to indicate + * no certificate. + */ + if (optional == OPTIONAL_PACKET && + ret == GNUTLS_E_WARNING_ALERT_RECEIVED && + gnutls_protocol_get_version (session) == GNUTLS_SSL3 && + gnutls_alert_get (session) == GNUTLS_A_SSL3_NO_CERTIFICATE) + { + + /* SSL3 does not send an empty certificate, + * but this alert. So we just ignore it. + */ + gnutls_assert (); + return 0; + } + + /* certificate was required + */ + if ((ret == GNUTLS_E_WARNING_ALERT_RECEIVED + || ret == GNUTLS_E_FATAL_ALERT_RECEIVED) + && optional == MANDATORY_PACKET) + { + gnutls_assert (); + return GNUTLS_E_NO_CERTIFICATE_FOUND; + } + + return ret; + } + + if (ret == 0 && datasize == 0 && optional == OPTIONAL_PACKET) + { + /* Client has not sent the certificate message. + * well I'm not sure we should accept this + * behaviour. + */ + gnutls_assert (); + return 0; + } + ret = + session->internals.auth_struct-> + gnutls_process_client_certificate (session, data, datasize); + + gnutls_free (data); + if (ret < 0 && ret != GNUTLS_E_NO_CERTIFICATE_FOUND) + { + gnutls_assert (); + return ret; + } + + /* ok we should expect a certificate verify message now + */ + if (ret == GNUTLS_E_NO_CERTIFICATE_FOUND && optional == OPTIONAL_PACKET) + ret = 0; + else + session->key->certificate_requested = 1; + + } + + return ret; +} + +int +_gnutls_recv_server_certificate (gnutls_session_t session) +{ + int datasize; + opaque *data; + int ret = 0; + + if (session->internals.auth_struct-> + gnutls_process_server_certificate != NULL) + { + + ret = + _gnutls_recv_handshake (session, &data, + &datasize, + GNUTLS_HANDSHAKE_CERTIFICATE_PKT, + MANDATORY_PACKET); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + ret = + session->internals.auth_struct-> + gnutls_process_server_certificate (session, data, datasize); + gnutls_free (data); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + } + + return ret; +} + + +/* Recv the client certificate verify. This packet may not + * arrive if the peer did not send us a certificate. + */ +int +_gnutls_recv_client_certificate_verify_message (gnutls_session_t session) +{ + uint8_t *data; + int datasize; + int ret = 0; + + + if (session->internals.auth_struct->gnutls_process_client_cert_vrfy != NULL) + { + + if (session->internals.send_cert_req == 0 || + session->key->certificate_requested == 0) + { + return 0; + } + + ret = + _gnutls_recv_handshake (session, &data, + &datasize, + GNUTLS_HANDSHAKE_CERTIFICATE_VERIFY, + OPTIONAL_PACKET); + if (ret < 0) + return ret; + + if (ret == 0 && datasize == 0 + && session->internals.send_cert_req == GNUTLS_CERT_REQUIRE) + { + /* certificate was required */ + gnutls_assert (); + return GNUTLS_E_NO_CERTIFICATE_FOUND; + } + + ret = + session->internals.auth_struct-> + gnutls_process_client_cert_vrfy (session, data, datasize); + gnutls_free (data); + if (ret < 0) + return ret; + + } + + return ret; +} diff --git a/src/daemon/https/tls/gnutls_kx.h b/src/daemon/https/tls/gnutls_kx.h new file mode 100644 index 00000000..45ba68bd --- /dev/null +++ b/src/daemon/https/tls/gnutls_kx.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +int _gnutls_send_server_kx_message (gnutls_session_t session, int again); +int _gnutls_send_client_kx_message (gnutls_session_t session, int again); +int _gnutls_recv_server_kx_message (gnutls_session_t session); +int _gnutls_recv_client_kx_message (gnutls_session_t session); +int _gnutls_send_client_certificate_verify (gnutls_session_t session, + int again); +int _gnutls_send_server_certificate (gnutls_session_t session, int again); +int _gnutls_generate_master (gnutls_session_t session, int keep_premaster); +int _gnutls_recv_client_certificate (gnutls_session_t session); +int _gnutls_recv_server_certificate (gnutls_session_t session); +int _gnutls_send_client_certificate (gnutls_session_t session, int again); +int _gnutls_recv_server_certificate_request (gnutls_session_t session); +int _gnutls_send_server_certificate_request (gnutls_session_t session, + int again); +int _gnutls_recv_client_certificate_verify_message (gnutls_session_t session); diff --git a/src/daemon/https/tls/gnutls_mem.c b/src/daemon/https/tls/gnutls_mem.c new file mode 100644 index 00000000..91a6fa3c --- /dev/null +++ b/src/daemon/https/tls/gnutls_mem.c @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2001, 2002, 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +#include +#include +#include + +gnutls_alloc_function gnutls_secure_malloc = malloc; +gnutls_alloc_function gnutls_malloc = malloc; +gnutls_free_function gnutls_free = free; +gnutls_realloc_function gnutls_realloc = realloc; + +void *(*gnutls_calloc) (size_t, size_t) = calloc; +char *(*gnutls_strdup) (const char *) = _gnutls_strdup; + +int +_gnutls_is_secure_mem_null (const void *ign) +{ + return 0; +} + +int (*_gnutls_is_secure_memory) (const void *) = _gnutls_is_secure_mem_null; + + +void * +_gnutls_calloc (size_t nmemb, size_t size) +{ + void *ret; + size *= nmemb; + ret = gnutls_malloc (size); + if (ret != NULL) + memset (ret, 0, size); + return ret; +} + +svoid * +gnutls_secure_calloc (size_t nmemb, size_t size) +{ + svoid *ret; + size *= nmemb; + ret = gnutls_secure_malloc (size); + if (ret != NULL) + memset (ret, 0, size); + return ret; +} + +/* This realloc will free ptr in case realloc + * fails. + */ +void * +gnutls_realloc_fast (void *ptr, size_t size) +{ + void *ret; + + if (size == 0) + return ptr; + + ret = gnutls_realloc (ptr, size); + if (ret == NULL) + { + gnutls_free (ptr); + } + + return ret; +} + +char * +_gnutls_strdup (const char *str) +{ + size_t siz = strlen (str) + 1; + char *ret; + + ret = gnutls_malloc (siz); + if (ret != NULL) + memcpy (ret, str, siz); + return ret; +} + + +#if 0 +/* don't use them. They are included for documentation. + */ + +/** + * gnutls_malloc - Allocates and returns data + * + * This function will allocate 's' bytes data, and + * return a pointer to memory. This function is supposed + * to be used by callbacks. + * + * The allocation function used is the one set by gnutls_global_set_mem_functions(). + * + **/ +void * +gnutls_malloc (size_t s) +{ +} + +/** + * gnutls_free - Returns a free() like function + * @d: pointer to memory + * + * This function will free data pointed by ptr. + * + * The deallocation function used is the one set by gnutls_global_set_mem_functions(). + * + **/ +void +gnutls_free (void *ptr) +{ +} + +#endif diff --git a/src/daemon/https/tls/gnutls_mem.h b/src/daemon/https/tls/gnutls_mem.h new file mode 100644 index 00000000..f76081e5 --- /dev/null +++ b/src/daemon/https/tls/gnutls_mem.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +#ifndef GNUTLS_MEM_H +# define GNUTLS_MEM_H + +#ifdef USE_DMALLOC +# include +#endif + +typedef void svoid; /* for functions that allocate using gnutls_secure_malloc */ + +/* Use gnutls_afree() when calling alloca, or + * memory leaks may occur in systems which do not + * support alloca. + */ +#ifdef USE_EFENCE +# define gnutls_alloca gnutls_malloc +# define gnutls_afree gnutls_free +#endif + +#ifdef HAVE_ALLOCA +# ifdef HAVE_ALLOCA_H +# include +# endif +# ifndef gnutls_alloca +# define gnutls_alloca alloca +# define gnutls_afree(x) +# endif +#else +# ifndef gnutls_alloca +# define gnutls_alloca gnutls_malloc +# define gnutls_afree gnutls_free +# endif +#endif /* HAVE_ALLOCA */ + +extern int (*_gnutls_is_secure_memory) (const void *); + +/* this realloc function will return ptr if size==0, and + * will free the ptr if the new allocation failed. + */ +void *gnutls_realloc_fast (void *ptr, size_t size); + +svoid *gnutls_secure_calloc (size_t nmemb, size_t size); + +void *_gnutls_calloc (size_t nmemb, size_t size); +char *_gnutls_strdup (const char *); + +#endif /* GNUTLS_MEM_H */ diff --git a/src/daemon/https/tls/gnutls_mpi.c b/src/daemon/https/tls/gnutls_mpi.c new file mode 100644 index 00000000..0d807a18 --- /dev/null +++ b/src/daemon/https/tls/gnutls_mpi.c @@ -0,0 +1,285 @@ +/* + * Copyright (C) 2001, 2002, 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +/* Here lie everything that has to do with large numbers, libgcrypt and + * other stuff that didn't fit anywhere else. + */ + +#include +#include +#include +#include + +/* Functions that refer to the libgcrypt library. + */ + +void +_gnutls_mpi_release (mpi_t * x) +{ + if (*x == NULL) + return; + gcry_mpi_release (*x); + *x = NULL; +} + +/* returns zero on success + */ +int +_gnutls_mpi_scan (mpi_t * ret_mpi, const opaque * buffer, size_t * nbytes) +{ + int ret; + + ret = gcry_mpi_scan (ret_mpi, GCRYMPI_FMT_USG, buffer, *nbytes, nbytes); + if (ret) + return GNUTLS_E_MPI_SCAN_FAILED; + + return 0; +} + +/* returns zero on success. Fails if the number is zero. + */ +int +_gnutls_mpi_scan_nz (mpi_t * ret_mpi, const opaque * buffer, size_t * nbytes) +{ + int ret; + + ret = gcry_mpi_scan (ret_mpi, GCRYMPI_FMT_USG, buffer, *nbytes, nbytes); + if (ret) + return GNUTLS_E_MPI_SCAN_FAILED; + + /* MPIs with 0 bits are illegal + */ + if (_gnutls_mpi_get_nbits (*ret_mpi) == 0) + { + _gnutls_mpi_release (ret_mpi); + return GNUTLS_E_MPI_SCAN_FAILED; + } + + return 0; +} + +int +_gnutls_mpi_scan_pgp (mpi_t * ret_mpi, const opaque * buffer, size_t * nbytes) +{ + int ret; + ret = gcry_mpi_scan (ret_mpi, GCRYMPI_FMT_PGP, buffer, *nbytes, nbytes); + if (ret) + return GNUTLS_E_MPI_SCAN_FAILED; + + /* MPIs with 0 bits are illegal + */ + if (_gnutls_mpi_get_nbits (*ret_mpi) == 0) + { + _gnutls_mpi_release (ret_mpi); + return GNUTLS_E_MPI_SCAN_FAILED; + } + + return 0; +} + +int +_gnutls_mpi_print (void *buffer, size_t * nbytes, const mpi_t a) +{ + int ret; + + if (nbytes == NULL || a == NULL) + return GNUTLS_E_INVALID_REQUEST; + + ret = gcry_mpi_print (GCRYMPI_FMT_USG, buffer, *nbytes, nbytes, a); + if (!ret) + return 0; + + return GNUTLS_E_MPI_PRINT_FAILED; +} + +/* Always has the first bit zero */ +int +_gnutls_mpi_print_lz (void *buffer, size_t * nbytes, const mpi_t a) +{ + int ret; + + if (nbytes == NULL || a == NULL) + return GNUTLS_E_INVALID_REQUEST; + + ret = gcry_mpi_print (GCRYMPI_FMT_STD, buffer, *nbytes, nbytes, a); + if (!ret) + return 0; + + return GNUTLS_E_MPI_PRINT_FAILED; +} + +/* Always has the first bit zero */ +int +_gnutls_mpi_dprint_lz (gnutls_datum_t * dest, const mpi_t a) +{ + int ret; + opaque *buf = NULL; + size_t bytes = 0; + + if (dest == NULL || a == NULL) + return GNUTLS_E_INVALID_REQUEST; + + gcry_mpi_print (GCRYMPI_FMT_STD, NULL, 0, &bytes, a); + + if (bytes != 0) + buf = gnutls_malloc (bytes); + if (buf == NULL) + return GNUTLS_E_MEMORY_ERROR; + + ret = gcry_mpi_print (GCRYMPI_FMT_STD, buf, bytes, &bytes, a); + if (!ret) + { + dest->data = buf; + dest->size = bytes; + return 0; + } + + gnutls_free (buf); + return GNUTLS_E_MPI_PRINT_FAILED; +} + +int +_gnutls_mpi_dprint (gnutls_datum_t * dest, const mpi_t a) +{ + int ret; + opaque *buf = NULL; + size_t bytes = 0; + + if (dest == NULL || a == NULL) + return GNUTLS_E_INVALID_REQUEST; + + gcry_mpi_print (GCRYMPI_FMT_USG, NULL, 0, &bytes, a); + + if (bytes != 0) + buf = gnutls_malloc (bytes); + if (buf == NULL) + return GNUTLS_E_MEMORY_ERROR; + + ret = gcry_mpi_print (GCRYMPI_FMT_USG, buf, bytes, &bytes, a); + if (!ret) + { + dest->data = buf; + dest->size = bytes; + return 0; + } + + gnutls_free (buf); + return GNUTLS_E_MPI_PRINT_FAILED; +} + + +/* this function reads an integer + * from asn1 structs. Combines the read and mpi_scan + * steps. + */ +int +_gnutls_x509_read_int (ASN1_TYPE node, const char *value, mpi_t * ret_mpi) +{ + int result; + size_t s_len; + opaque *tmpstr = NULL; + int tmpstr_size; + + tmpstr_size = 0; + result = asn1_read_value (node, value, NULL, &tmpstr_size); + if (result != ASN1_MEM_ERROR) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + tmpstr = gnutls_alloca (tmpstr_size); + if (tmpstr == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + result = asn1_read_value (node, value, tmpstr, &tmpstr_size); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + gnutls_afree (tmpstr); + return _gnutls_asn2err (result); + } + + s_len = tmpstr_size; + if (_gnutls_mpi_scan (ret_mpi, tmpstr, &s_len) != 0) + { + gnutls_assert (); + gnutls_afree (tmpstr); + return GNUTLS_E_MPI_SCAN_FAILED; + } + + gnutls_afree (tmpstr); + + return 0; +} + +/* Writes the specified integer into the specified node. + */ +int +_gnutls_x509_write_int (ASN1_TYPE node, const char *value, mpi_t mpi, int lz) +{ + opaque *tmpstr; + size_t s_len; + int result; + + s_len = 0; + if (lz) + result = _gnutls_mpi_print_lz (NULL, &s_len, mpi); + else + result = _gnutls_mpi_print (NULL, &s_len, mpi); + + tmpstr = gnutls_alloca (s_len); + if (tmpstr == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + if (lz) + result = _gnutls_mpi_print_lz (tmpstr, &s_len, mpi); + else + result = _gnutls_mpi_print (tmpstr, &s_len, mpi); + + if (result != 0) + { + gnutls_assert (); + gnutls_afree (tmpstr); + return GNUTLS_E_MPI_PRINT_FAILED; + } + + result = asn1_write_value (node, value, tmpstr, s_len); + + gnutls_afree (tmpstr); + + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + return 0; +} diff --git a/src/daemon/https/tls/gnutls_mpi.h b/src/daemon/https/tls/gnutls_mpi.h new file mode 100644 index 00000000..de2a6f20 --- /dev/null +++ b/src/daemon/https/tls/gnutls_mpi.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +#ifndef GNUTLS_MPI_H +# define GNUTLS_MPI_H + +# include +# include +# include +/* lgl */ +# include "gc.h" + +typedef gcry_mpi_t mpi_t; + +#define _gnutls_mpi_cmp gcry_mpi_cmp +#define _gnutls_mpi_cmp_ui gcry_mpi_cmp_ui +#define _gnutls_mpi_mod gcry_mpi_mod +#define _gnutls_mpi_new gcry_mpi_new +#define _gnutls_mpi_snew gcry_mpi_snew +#define _gnutls_mpi_copy gcry_mpi_copy +#define _gnutls_mpi_set_ui gcry_mpi_set_ui +#define _gnutls_mpi_set gcry_mpi_set +#define _gnutls_mpi_randomize gcry_mpi_randomize +#define _gnutls_mpi_get_nbits gcry_mpi_get_nbits +#define _gnutls_mpi_powm gcry_mpi_powm +#define _gnutls_mpi_invm gcry_mpi_invm +#define _gnutls_mpi_addm gcry_mpi_addm +#define _gnutls_mpi_subm gcry_mpi_subm +#define _gnutls_mpi_sub_ui gcry_mpi_sub_ui +#define _gnutls_mpi_mulm gcry_mpi_mulm +#define _gnutls_mpi_mul gcry_mpi_mul +#define _gnutls_mpi_add gcry_mpi_add +#define _gnutls_mpi_add_ui gcry_mpi_add_ui +#define _gnutls_mpi_sub_ui gcry_mpi_sub_ui +#define _gnutls_mpi_mul_ui gcry_mpi_mul_ui +#define _gnutls_prime_check gcry_prime_check +#define _gnutls_mpi_div gcry_mpi_div + +#define _gnutls_mpi_alloc_like(x) _gnutls_mpi_new(_gnutls_mpi_get_nbits(x)) +#define _gnutls_mpi_salloc_like(x) _gnutls_mpi_snew(_gnutls_mpi_get_nbits(x)) + +void _gnutls_mpi_release (mpi_t * x); + +int _gnutls_mpi_scan_nz (mpi_t * ret_mpi, const opaque * buffer, + size_t * nbytes); +int _gnutls_mpi_scan (mpi_t * ret_mpi, const opaque * buffer, + size_t * nbytes); +int _gnutls_mpi_scan_pgp (mpi_t * ret_mpi, const opaque * buffer, + size_t * nbytes); + +int _gnutls_mpi_print (void *buffer, size_t * nbytes, const mpi_t a); +int _gnutls_mpi_print_lz (void *buffer, size_t * nbytes, const mpi_t a); + +int _gnutls_mpi_dprint_lz (gnutls_datum_t * dest, const mpi_t a); +int _gnutls_mpi_dprint (gnutls_datum_t * dest, const mpi_t a); + +#endif diff --git a/src/daemon/https/tls/gnutls_num.c b/src/daemon/https/tls/gnutls_num.c new file mode 100644 index 00000000..474cb73b --- /dev/null +++ b/src/daemon/https/tls/gnutls_num.c @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +/* This file contains the functions needed for 64 bit integer support in + * TLS, and functions which ease the access to TLS vectors (data of given size). + */ + +#include +#include +#include + +/* This function will add one to uint64 x. + * Returns 0 on success, or -1 if the uint64 max limit + * has been reached. + */ +int +_gnutls_uint64pp (uint64 * x) +{ + register int i, y = 0; + + for (i = 7; i >= 0; i--) + { + y = 0; + if (x->i[i] == 0xff) + { + x->i[i] = 0; + y = 1; + } + else + x->i[i]++; + + if (y == 0) + break; + } + if (y != 0) + return -1; /* over 64 bits! WOW */ + + return 0; +} + +uint32_t +_gnutls_uint24touint32 (uint24 num) +{ + uint32_t ret = 0; + + ((uint8_t *) & ret)[1] = num.pint[0]; + ((uint8_t *) & ret)[2] = num.pint[1]; + ((uint8_t *) & ret)[3] = num.pint[2]; + return ret; +} + +uint24 +_gnutls_uint32touint24 (uint32_t num) +{ + uint24 ret; + + ret.pint[0] = ((uint8_t *) & num)[1]; + ret.pint[1] = ((uint8_t *) & num)[2]; + ret.pint[2] = ((uint8_t *) & num)[3]; + return ret; + +} + +/* data should be at least 3 bytes */ +uint32_t +_gnutls_read_uint24 (const opaque * data) +{ + uint32_t res; + uint24 num; + + num.pint[0] = data[0]; + num.pint[1] = data[1]; + num.pint[2] = data[2]; + + res = _gnutls_uint24touint32 (num); +#ifndef WORDS_BIGENDIAN + res = byteswap32 (res); +#endif + return res; +} + +void +_gnutls_write_uint24 (uint32_t num, opaque * data) +{ + uint24 tmp; + +#ifndef WORDS_BIGENDIAN + num = byteswap32 (num); +#endif + tmp = _gnutls_uint32touint24 (num); + + data[0] = tmp.pint[0]; + data[1] = tmp.pint[1]; + data[2] = tmp.pint[2]; +} + +uint32_t +_gnutls_read_uint32 (const opaque * data) +{ + uint32_t res; + + memcpy (&res, data, sizeof (uint32_t)); +#ifndef WORDS_BIGENDIAN + res = byteswap32 (res); +#endif + return res; +} + +void +_gnutls_write_uint32 (uint32_t num, opaque * data) +{ + +#ifndef WORDS_BIGENDIAN + num = byteswap32 (num); +#endif + memcpy (data, &num, sizeof (uint32_t)); +} + +uint16_t +_gnutls_read_uint16 (const opaque * data) +{ + uint16_t res; + memcpy (&res, data, sizeof (uint16_t)); +#ifndef WORDS_BIGENDIAN + res = byteswap16 (res); +#endif + return res; +} + +void +_gnutls_write_uint16 (uint16_t num, opaque * data) +{ + +#ifndef WORDS_BIGENDIAN + num = byteswap16 (num); +#endif + memcpy (data, &num, sizeof (uint16_t)); +} + +uint32_t +_gnutls_conv_uint32 (uint32_t data) +{ +#ifndef WORDS_BIGENDIAN + return byteswap32 (data); +#else + return data; +#endif +} + +uint16_t +_gnutls_conv_uint16 (uint16_t data) +{ +#ifndef WORDS_BIGENDIAN + return byteswap16 (data); +#else + return data; +#endif +} + +uint32_t +_gnutls_uint64touint32 (const uint64 * num) +{ + uint32_t ret; + + memcpy (&ret, &num->i[4], 4); +#ifndef WORDS_BIGENDIAN + ret = byteswap32 (ret); +#endif + + return ret; +} diff --git a/src/daemon/https/tls/gnutls_num.h b/src/daemon/https/tls/gnutls_num.h new file mode 100644 index 00000000..3ab226ae --- /dev/null +++ b/src/daemon/https/tls/gnutls_num.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +#include + +#define rotl32(x,n) (((x) << ((uint16_t)(n))) | ((x) >> (32 - (uint16_t)(n)))) +#define rotr32(x,n) (((x) >> ((uint16_t)(n))) | ((x) << (32 - (uint16_t)(n)))) +#define rotl16(x,n) (((x) << ((uint16_t)(n))) | ((x) >> (16 - (uint16_t)(n)))) +#define rotr16(x,n) (((x) >> ((uint16_t)(n))) | ((x) << (16 - (uint16_t)(n)))) + +#define byteswap16(x) ((rotl16(x, 8) & 0x00ff) | (rotr16(x, 8) & 0xff00)) +#define byteswap32(x) ((rotl32(x, 8) & 0x00ff00ffUL) | (rotr32(x, 8) & 0xff00ff00UL)) + +uint32_t _gnutls_uint24touint32 (uint24 num); +uint24 _gnutls_uint32touint24 (uint32_t num); +uint32_t _gnutls_read_uint32 (const opaque * data); +uint16_t _gnutls_read_uint16 (const opaque * data); +uint32_t _gnutls_conv_uint32 (uint32_t data); +uint16_t _gnutls_conv_uint16 (uint16_t data); +uint32_t _gnutls_read_uint24 (const opaque * data); +void _gnutls_write_uint24 (uint32_t num, opaque * data); +void _gnutls_write_uint32 (uint32_t num, opaque * data); +void _gnutls_write_uint16 (uint16_t num, opaque * data); +uint32_t _gnutls_uint64touint32 (const uint64 *); + +int _gnutls_uint64pp (uint64 *); +# define _gnutls_uint64zero(x) x.i[0] = x.i[1] = x.i[2] = x.i[3] = x.i[4] = x.i[5] = x.i[6] = x.i[7] = 0 +# define UINT64DATA(x) x.i diff --git a/src/daemon/https/tls/gnutls_pk.c b/src/daemon/https/tls/gnutls_pk.c new file mode 100644 index 00000000..cfec9463 --- /dev/null +++ b/src/daemon/https/tls/gnutls_pk.c @@ -0,0 +1,915 @@ +/* + * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +/* This file contains the functions needed for RSA/DSA public key + * encryption and signatures. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "debug.h" +#include + +/* x509 */ +#include "common.h" +#include "mpi.h" + +static int _gnutls_pk_encrypt (int algo, mpi_t * resarr, mpi_t data, + mpi_t * pkey, int pkey_len); +static int _gnutls_pk_sign (int algo, mpi_t * data, mpi_t hash, + mpi_t * pkey, int); +static int _gnutls_pk_verify (int algo, mpi_t hash, mpi_t * data, + mpi_t * pkey, int); +static int _gnutls_pk_decrypt (int algo, mpi_t * resarr, mpi_t data, + mpi_t * pkey, int); + + +/* Do PKCS-1 RSA encryption. + * params is modulus, public exp. + */ +int +_gnutls_pkcs1_rsa_encrypt (gnutls_datum_t * ciphertext, + const gnutls_datum_t * plaintext, + mpi_t * params, unsigned params_len, + unsigned btype) +{ + unsigned int i, pad; + int ret; + mpi_t m, res; + opaque *edata, *ps; + size_t k, psize; + size_t mod_bits; + + mod_bits = _gnutls_mpi_get_nbits (params[0]); + k = mod_bits / 8; + if (mod_bits % 8 != 0) + k++; + + if (plaintext->size > k - 11) + { + gnutls_assert (); + return GNUTLS_E_PK_ENCRYPTION_FAILED; + } + + edata = gnutls_alloca (k); + if (edata == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + /* EB = 00||BT||PS||00||D + * (use block type 'btype') + */ + + edata[0] = 0; + edata[1] = btype; + psize = k - 3 - plaintext->size; + + ps = &edata[2]; + switch (btype) + { + case 2: + /* using public key */ + if (params_len < RSA_PUBLIC_PARAMS) + { + gnutls_assert (); + gnutls_afree (edata); + return GNUTLS_E_INTERNAL_ERROR; + } + + if (gc_pseudo_random (ps, psize) != GC_OK) + { + gnutls_assert (); + gnutls_afree (edata); + return GNUTLS_E_RANDOM_FAILED; + } + for (i = 0; i < psize; i++) + while (ps[i] == 0) + { + if (gc_pseudo_random (&ps[i], 1) != GC_OK) + { + gnutls_assert (); + gnutls_afree (edata); + return GNUTLS_E_RANDOM_FAILED; + } + } + break; + case 1: + /* using private key */ + + if (params_len < RSA_PRIVATE_PARAMS) + { + gnutls_assert (); + gnutls_afree (edata); + return GNUTLS_E_INTERNAL_ERROR; + } + + for (i = 0; i < psize; i++) + ps[i] = 0xff; + break; + default: + gnutls_assert (); + gnutls_afree (edata); + return GNUTLS_E_INTERNAL_ERROR; + } + + ps[psize] = 0; + memcpy (&ps[psize + 1], plaintext->data, plaintext->size); + + if (_gnutls_mpi_scan_nz (&m, edata, &k) != 0) + { + gnutls_assert (); + gnutls_afree (edata); + return GNUTLS_E_MPI_SCAN_FAILED; + } + gnutls_afree (edata); + + if (btype == 2) /* encrypt */ + ret = _gnutls_pk_encrypt (GCRY_PK_RSA, &res, m, params, params_len); + else /* sign */ + ret = _gnutls_pk_sign (GCRY_PK_RSA, &res, m, params, params_len); + + _gnutls_mpi_release (&m); + + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + _gnutls_mpi_print (NULL, &psize, res); + + if (psize < k) + { + /* padding psize */ + pad = k - psize; + psize = k; + } + else if (psize == k) + { + pad = 0; + } + else + { /* psize > k !!! */ + /* This is an impossible situation */ + gnutls_assert (); + _gnutls_mpi_release (&res); + return GNUTLS_E_INTERNAL_ERROR; + } + + ciphertext->data = gnutls_malloc (psize); + if (ciphertext->data == NULL) + { + gnutls_assert (); + _gnutls_mpi_release (&res); + return GNUTLS_E_MEMORY_ERROR; + } + _gnutls_mpi_print (&ciphertext->data[pad], &psize, res); + for (i = 0; i < pad; i++) + ciphertext->data[i] = 0; + + ciphertext->size = k; + + _gnutls_mpi_release (&res); + + return 0; +} + + +/* Do PKCS-1 RSA decryption. + * params is modulus, public exp., private key + * Can decrypt block type 1 and type 2 packets. + */ +int +_gnutls_pkcs1_rsa_decrypt (gnutls_datum_t * plaintext, + const gnutls_datum_t * ciphertext, + mpi_t * params, unsigned params_len, + unsigned btype) +{ + unsigned k, i; + int ret; + mpi_t c, res; + opaque *edata; + size_t esize, mod_bits; + + mod_bits = _gnutls_mpi_get_nbits (params[0]); + k = mod_bits / 8; + if (mod_bits % 8 != 0) + k++; + + esize = ciphertext->size; + + if (esize != k) + { + gnutls_assert (); + return GNUTLS_E_PK_DECRYPTION_FAILED; + } + + if (_gnutls_mpi_scan_nz (&c, ciphertext->data, &esize) != 0) + { + gnutls_assert (); + return GNUTLS_E_MPI_SCAN_FAILED; + } + + /* we can use btype to see if the private key is + * available. + */ + if (btype == 2) + ret = _gnutls_pk_decrypt (GCRY_PK_RSA, &res, c, params, params_len); + else + { + ret = _gnutls_pk_encrypt (GCRY_PK_RSA, &res, c, params, params_len); + } + _gnutls_mpi_release (&c); + + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + _gnutls_mpi_print (NULL, &esize, res); + edata = gnutls_alloca (esize + 1); + if (edata == NULL) + { + gnutls_assert (); + _gnutls_mpi_release (&res); + return GNUTLS_E_MEMORY_ERROR; + } + _gnutls_mpi_print (&edata[1], &esize, res); + + _gnutls_mpi_release (&res); + + /* EB = 00||BT||PS||00||D + * (use block type 'btype') + * + * From now on, return GNUTLS_E_DECRYPTION_FAILED on errors, to + * avoid attacks similar to the one described by Bleichenbacher in: + * "Chosen Ciphertext Attacks against Protocols Based on RSA + * Encryption Standard PKCS #1". + */ + + + edata[0] = 0; + esize++; + + if (edata[0] != 0 || edata[1] != btype) + { + gnutls_assert (); + gnutls_afree (edata); + return GNUTLS_E_DECRYPTION_FAILED; + } + + ret = GNUTLS_E_DECRYPTION_FAILED; + switch (btype) + { + case 2: + for (i = 2; i < esize; i++) + { + if (edata[i] == 0) + { + ret = 0; + break; + } + } + break; + case 1: + for (i = 2; i < esize; i++) + { + if (edata[i] == 0 && i > 2) + { + ret = 0; + break; + } + if (edata[i] != 0xff) + { + _gnutls_handshake_log ("PKCS #1 padding error"); + /* PKCS #1 padding error. Don't use + GNUTLS_E_PKCS1_WRONG_PAD here. */ + break; + } + } + break; + default: + gnutls_assert (); + gnutls_afree (edata); + break; + } + i++; + + if (ret < 0) + { + gnutls_assert (); + gnutls_afree (edata); + return GNUTLS_E_DECRYPTION_FAILED; + } + + if (_gnutls_sset_datum (plaintext, &edata[i], esize - i) < 0) + { + gnutls_assert (); + gnutls_afree (edata); + return GNUTLS_E_MEMORY_ERROR; + } + + gnutls_afree (edata); + + return 0; +} + + +int +_gnutls_rsa_verify (const gnutls_datum_t * vdata, + const gnutls_datum_t * ciphertext, mpi_t * params, + int params_len, int btype) +{ + + gnutls_datum_t plain; + int ret; + + /* decrypt signature */ + if ((ret = + _gnutls_pkcs1_rsa_decrypt (&plain, ciphertext, params, params_len, + btype)) < 0) + { + gnutls_assert (); + return ret; + } + + if (plain.size != vdata->size) + { + gnutls_assert (); + _gnutls_free_datum (&plain); + return GNUTLS_E_PK_SIG_VERIFY_FAILED; + } + + if (memcmp (plain.data, vdata->data, plain.size) != 0) + { + gnutls_assert (); + _gnutls_free_datum (&plain); + return GNUTLS_E_PK_SIG_VERIFY_FAILED; + } + + _gnutls_free_datum (&plain); + + return 0; /* ok */ +} + +/* encodes the Dss-Sig-Value structure + */ +static int +encode_ber_rs (gnutls_datum_t * sig_value, mpi_t r, mpi_t s) +{ + ASN1_TYPE sig; + int result, tot_len; + + if ((result = + asn1_create_element (_gnutls_get_gnutls_asn (), + "GNUTLS.DSASignatureValue", + &sig)) != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + result = _gnutls_x509_write_int (sig, "r", r, 1); + if (result < 0) + { + gnutls_assert (); + asn1_delete_structure (&sig); + return result; + } + + result = _gnutls_x509_write_int (sig, "s", s, 1); + if (result < 0) + { + gnutls_assert (); + asn1_delete_structure (&sig); + return result; + } + + tot_len = 0; + + result = _gnutls_x509_der_encode (sig, "", sig_value, 0); + + asn1_delete_structure (&sig); + + if (result < 0) + { + gnutls_assert (); + return result; + } + + return 0; +} + + +/* Do DSA signature calculation. params is p, q, g, y, x in that order. + */ +int +_gnutls_dsa_sign (gnutls_datum_t * signature, + const gnutls_datum_t * hash, mpi_t * params, + unsigned params_len) +{ + mpi_t rs[2], mdata; + int ret; + size_t k; + + k = hash->size; + if (k < 20) + { /* SHA1 or better only */ + gnutls_assert (); + return GNUTLS_E_PK_SIGN_FAILED; + } + + if (_gnutls_mpi_scan_nz (&mdata, hash->data, &k) != 0) + { + gnutls_assert (); + return GNUTLS_E_MPI_SCAN_FAILED; + } + + ret = _gnutls_pk_sign (GCRY_PK_DSA, rs, mdata, params, params_len); + /* rs[0], rs[1] now hold r,s */ + _gnutls_mpi_release (&mdata); + + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + ret = encode_ber_rs (signature, rs[0], rs[1]); + + /* free r,s */ + _gnutls_mpi_release (&rs[0]); + _gnutls_mpi_release (&rs[1]); + + if (ret != 0) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + return 0; +} + +/* decodes the Dss-Sig-Value structure + */ +static int +decode_ber_rs (const gnutls_datum_t * sig_value, mpi_t * r, mpi_t * s) +{ + ASN1_TYPE sig; + int result; + + if ((result = + asn1_create_element (_gnutls_get_gnutls_asn (), + "GNUTLS.DSASignatureValue", + &sig)) != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + result = asn1_der_decoding (&sig, sig_value->data, sig_value->size, NULL); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + asn1_delete_structure (&sig); + return _gnutls_asn2err (result); + } + + result = _gnutls_x509_read_int (sig, "r", r); + if (result < 0) + { + gnutls_assert (); + asn1_delete_structure (&sig); + return result; + } + + result = _gnutls_x509_read_int (sig, "s", s); + if (result < 0) + { + gnutls_assert (); + _gnutls_mpi_release (s); + asn1_delete_structure (&sig); + return result; + } + + asn1_delete_structure (&sig); + + return 0; +} + +/* params is p, q, g, y in that order + */ +int +_gnutls_dsa_verify (const gnutls_datum_t * vdata, + const gnutls_datum_t * sig_value, mpi_t * params, + int params_len) +{ + + mpi_t mdata; + int ret; + size_t k; + mpi_t rs[2]; + + if (vdata->size != 20) + { /* sha-1 only */ + gnutls_assert (); + return GNUTLS_E_PK_SIG_VERIFY_FAILED; + } + + if (decode_ber_rs (sig_value, &rs[0], &rs[1]) != 0) + { + gnutls_assert (); + return GNUTLS_E_MPI_SCAN_FAILED; + } + + k = vdata->size; + if (_gnutls_mpi_scan_nz (&mdata, vdata->data, &k) != 0) + { + gnutls_assert (); + _gnutls_mpi_release (&rs[0]); + _gnutls_mpi_release (&rs[1]); + return GNUTLS_E_MPI_SCAN_FAILED; + } + + /* decrypt signature */ + ret = _gnutls_pk_verify (GCRY_PK_DSA, mdata, rs, params, params_len); + _gnutls_mpi_release (&mdata); + _gnutls_mpi_release (&rs[0]); + _gnutls_mpi_release (&rs[1]); + + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + return 0; /* ok */ +} + + +/* this is taken from gnupg + */ + +/**************** + * Emulate our old PK interface here - sometime in the future we might + * change the internal design to directly fit to libgcrypt. + */ +static int +_gnutls_pk_encrypt (int algo, mpi_t * resarr, mpi_t data, + mpi_t * pkey, int pkey_len) +{ + gcry_sexp_t s_ciph, s_data, s_pkey; + int rc = -1; + + /* make a sexp from pkey */ + switch (algo) + { + case GCRY_PK_RSA: + if (pkey_len >= 2) + rc = gcry_sexp_build (&s_pkey, NULL, + "(public-key(rsa(n%m)(e%m)))", + pkey[0], pkey[1]); + break; + + default: + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + if (rc != 0) + { + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + /* put the data into a simple list */ + if (gcry_sexp_build (&s_data, NULL, "%m", data)) + { + gnutls_assert (); + gcry_sexp_release (s_pkey); + return GNUTLS_E_INTERNAL_ERROR; + } + + /* pass it to libgcrypt */ + rc = gcry_pk_encrypt (&s_ciph, s_data, s_pkey); + gcry_sexp_release (s_data); + gcry_sexp_release (s_pkey); + + if (rc != 0) + { + gnutls_assert (); + return GNUTLS_E_PK_ENCRYPTION_FAILED; + + } + else + { /* add better error handling or make gnupg use S-Exp directly */ + gcry_sexp_t list = gcry_sexp_find_token (s_ciph, "a", 0); + if (list == NULL) + { + gnutls_assert (); + gcry_sexp_release (s_ciph); + return GNUTLS_E_INTERNAL_ERROR; + } + + resarr[0] = gcry_sexp_nth_mpi (list, 1, 0); + gcry_sexp_release (list); + + if (resarr[0] == NULL) + { + gnutls_assert (); + gcry_sexp_release (s_ciph); + return GNUTLS_E_INTERNAL_ERROR; + } + } + + gcry_sexp_release (s_ciph); + return rc; +} + +static int +_gnutls_pk_decrypt (int algo, mpi_t * resarr, mpi_t data, mpi_t * pkey, + int pkey_len) +{ + gcry_sexp_t s_plain, s_data, s_pkey; + int rc = -1; + + /* make a sexp from pkey */ + switch (algo) + { + case GCRY_PK_RSA: + if (pkey_len >= 6) + rc = gcry_sexp_build (&s_pkey, NULL, + "(private-key(rsa((n%m)(e%m)(d%m)(p%m)(q%m)(u%m))))", + pkey[0], pkey[1], pkey[2], pkey[3], + pkey[4], pkey[5]); + break; + + default: + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + if (rc != 0) + { + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + /* put the data into a simple list */ + if (gcry_sexp_build (&s_data, NULL, "(enc-val(rsa(a%m)))", data)) + { + gnutls_assert (); + gcry_sexp_release (s_pkey); + return GNUTLS_E_INTERNAL_ERROR; + } + + /* pass it to libgcrypt */ + rc = gcry_pk_decrypt (&s_plain, s_data, s_pkey); + gcry_sexp_release (s_data); + gcry_sexp_release (s_pkey); + + if (rc != 0) + { + gnutls_assert (); + return GNUTLS_E_PK_DECRYPTION_FAILED; + + } + else + { /* add better error handling or make gnupg use S-Exp directly */ + resarr[0] = gcry_sexp_nth_mpi (s_plain, 0, 0); + + if (resarr[0] == NULL) + { + gnutls_assert (); + gcry_sexp_release (s_plain); + return GNUTLS_E_INTERNAL_ERROR; + } + } + + gcry_sexp_release (s_plain); + return rc; +} + + +/* in case of DSA puts into data, r,s + */ +static int +_gnutls_pk_sign (int algo, mpi_t * data, mpi_t hash, mpi_t * pkey, + int pkey_len) +{ + gcry_sexp_t s_hash, s_key, s_sig; + int rc = -1; + + /* make a sexp from pkey */ + switch (algo) + { + case GCRY_PK_DSA: + if (pkey_len >= 5) + rc = gcry_sexp_build (&s_key, NULL, + "(private-key(dsa(p%m)(q%m)(g%m)(y%m)(x%m)))", + pkey[0], pkey[1], pkey[2], pkey[3], pkey[4]); + else + { + gnutls_assert (); + } + + break; + case GCRY_PK_RSA: + if (pkey_len >= 6) + rc = gcry_sexp_build (&s_key, NULL, + "(private-key(rsa((n%m)(e%m)(d%m)(p%m)(q%m)(u%m))))", + pkey[0], pkey[1], pkey[2], pkey[3], + pkey[4], pkey[5]); + else + { + gnutls_assert (); + } + break; + + default: + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + if (rc != 0) + { + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + /* put the data into a simple list */ + if (gcry_sexp_build (&s_hash, NULL, "%m", hash)) + { + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + /* pass it to libgcrypt */ + rc = gcry_pk_sign (&s_sig, s_hash, s_key); + gcry_sexp_release (s_hash); + gcry_sexp_release (s_key); + + if (rc != 0) + { + gnutls_assert (); + return GNUTLS_E_PK_SIGN_FAILED; + + } + else + { + gcry_sexp_t list; + + if (algo == GCRY_PK_DSA) + { + list = gcry_sexp_find_token (s_sig, "r", 0); + if (list == NULL) + { + gnutls_assert (); + gcry_sexp_release (s_sig); + return GNUTLS_E_INTERNAL_ERROR; + } + + data[0] = gcry_sexp_nth_mpi (list, 1, 0); + gcry_sexp_release (list); + + list = gcry_sexp_find_token (s_sig, "s", 0); + if (list == NULL) + { + gnutls_assert (); + gcry_sexp_release (s_sig); + return GNUTLS_E_INTERNAL_ERROR; + } + + data[1] = gcry_sexp_nth_mpi (list, 1, 0); + gcry_sexp_release (list); + } + else + { /* GCRY_PK_RSA */ + list = gcry_sexp_find_token (s_sig, "s", 0); + if (list == NULL) + { + gnutls_assert (); + gcry_sexp_release (s_sig); + return GNUTLS_E_INTERNAL_ERROR; + } + + data[0] = gcry_sexp_nth_mpi (list, 1, 0); + gcry_sexp_release (list); + } + } + + gcry_sexp_release (s_sig); + return 0; +} + + +static int +_gnutls_pk_verify (int algo, mpi_t hash, mpi_t * data, + mpi_t * pkey, int pkey_len) +{ + gcry_sexp_t s_sig, s_hash, s_pkey; + int rc = -1; + + /* make a sexp from pkey */ + switch (algo) + { + case GCRY_PK_DSA: + if (pkey_len >= 4) + rc = gcry_sexp_build (&s_pkey, NULL, + "(public-key(dsa(p%m)(q%m)(g%m)(y%m)))", + pkey[0], pkey[1], pkey[2], pkey[3]); + break; + case GCRY_PK_RSA: + if (pkey_len >= 2) + rc = gcry_sexp_build (&s_pkey, NULL, + "(public-key(rsa(n%m)(e%m)))", + pkey[0], pkey[1]); + break; + + default: + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + if (rc != 0) + { + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + /* put the data into a simple list */ + if (gcry_sexp_build (&s_hash, NULL, "%m", hash)) + { + gnutls_assert (); + gcry_sexp_release (s_pkey); + return GNUTLS_E_INTERNAL_ERROR; + } + + switch (algo) + { + case GCRY_PK_DSA: + rc = gcry_sexp_build (&s_sig, NULL, + "(sig-val(dsa(r%m)(s%m)))", data[0], data[1]); + break; + case GCRY_PK_RSA: + rc = gcry_sexp_build (&s_sig, NULL, "(sig-val(rsa(s%m)))", data[0]); + break; + + default: + gnutls_assert (); + gcry_sexp_release (s_pkey); + gcry_sexp_release (s_hash); + return GNUTLS_E_INTERNAL_ERROR; + } + + if (rc != 0) + { + gnutls_assert (); + gcry_sexp_release (s_pkey); + gcry_sexp_release (s_hash); + return GNUTLS_E_INTERNAL_ERROR; + } + + rc = gcry_pk_verify (s_sig, s_hash, s_pkey); + + gcry_sexp_release (s_sig); + gcry_sexp_release (s_hash); + gcry_sexp_release (s_pkey); + + if (rc != 0) + { + gnutls_assert (); + return GNUTLS_E_PK_SIG_VERIFY_FAILED; + } + + return 0; +} diff --git a/src/daemon/https/tls/gnutls_pk.h b/src/daemon/https/tls/gnutls_pk.h new file mode 100644 index 00000000..e6f37b0e --- /dev/null +++ b/src/daemon/https/tls/gnutls_pk.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +#ifndef GNUTLS_PK_H +# define GNUTLS_PK_H + +int _gnutls_pkcs1_rsa_encrypt (gnutls_datum_t * ciphertext, + const gnutls_datum_t * plaintext, + mpi_t * params, unsigned params_len, + unsigned btype); +int _gnutls_dsa_sign (gnutls_datum_t * signature, + const gnutls_datum_t * plaintext, mpi_t * params, + unsigned params_len); +int _gnutls_pkcs1_rsa_decrypt (gnutls_datum_t * plaintext, + const gnutls_datum_t * ciphertext, + mpi_t * params, unsigned params_len, + unsigned btype); +int _gnutls_rsa_verify (const gnutls_datum_t * vdata, + const gnutls_datum_t * ciphertext, mpi_t * params, + int params_len, int btype); +int _gnutls_dsa_verify (const gnutls_datum_t * vdata, + const gnutls_datum_t * sig_value, mpi_t * params, + int params_len); + +#endif /* GNUTLS_PK_H */ diff --git a/src/daemon/https/tls/gnutls_priority.c b/src/daemon/https/tls/gnutls_priority.c new file mode 100644 index 00000000..69019191 --- /dev/null +++ b/src/daemon/https/tls/gnutls_priority.c @@ -0,0 +1,497 @@ +/* + * Copyright (C) 2004, 2005, 2006, 2007 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +/* Here lies the code of the gnutls_*_set_priority() functions. + */ + +#include "gnutls_int.h" +#include "gnutls_algorithms.h" +#include "gnutls_errors.h" +#include + +#define MAX_ELEMENTS 48 + +static void break_comma_list (char *etag, + char **broken_etag, + int *elements, int max_elements, char sep); + +/** + * gnutls_cipher_set_priority - Sets the priority on the ciphers supported by gnutls. + * @session: is a #gnutls_session_t structure. + * @list: is a 0 terminated list of gnutls_cipher_algorithm_t elements. + * + * Sets the priority on the ciphers supported by gnutls. + * Priority is higher for elements specified before others. + * After specifying the ciphers you want, you must append a 0. + * Note that the priority is set on the client. The server does + * not use the algorithm's priority except for disabling + * algorithms that were not specified. + * + * Returns 0 on success. + * + **/ +int +gnutls_cipher_set_priority (gnutls_session_t session, const int *list) +{ + int num = 0, i; + + while (list[num] != 0) + num++; + if (num > MAX_ALGOS) + num = MAX_ALGOS; + session->internals.priorities.cipher.algorithms = num; + + for (i = 0; i < num; i++) + { + session->internals.priorities.cipher.priority[i] = list[i]; + } + + return 0; +} + +inline static int +_set_priority (priority_st * st, const int *list) +{ + int num = 0, i; + + while (list[num] != 0) + num++; + if (num > MAX_ALGOS) + num = MAX_ALGOS; + st->algorithms = num; + + for (i = 0; i < num; i++) + { + st->priority[i] = list[i]; + } + + return 0; + +} + +/** + * gnutls_kx_set_priority - Sets the priority on the key exchange algorithms supported by gnutls. + * @session: is a #gnutls_session_t structure. + * @list: is a 0 terminated list of gnutls_kx_algorithm_t elements. + * + * Sets the priority on the key exchange algorithms supported by gnutls. + * Priority is higher for elements specified before others. + * After specifying the algorithms you want, you must append a 0. + * Note that the priority is set on the client. The server does + * not use the algorithm's priority except for disabling + * algorithms that were not specified. + * + * Returns 0 on success. + * + **/ +int +gnutls_kx_set_priority (gnutls_session_t session, const int *list) +{ + return _set_priority (&session->internals.priorities.kx, list); +} + +/** + * gnutls_mac_set_priority - Sets the priority on the mac algorithms supported by gnutls. + * @session: is a #gnutls_session_t structure. + * @list: is a 0 terminated list of gnutls_mac_algorithm_t elements. + * + * Sets the priority on the mac algorithms supported by gnutls. + * Priority is higher for elements specified before others. + * After specifying the algorithms you want, you must append a 0. + * Note that the priority is set on the client. The server does + * not use the algorithm's priority except for disabling + * algorithms that were not specified. + * + * Returns 0 on success. + * + **/ +int +gnutls_mac_set_priority (gnutls_session_t session, const int *list) +{ + return _set_priority (&session->internals.priorities.mac, list); +} + +/** + * gnutls_compression_set_priority - Sets the priority on the compression algorithms supported by gnutls. + * @session: is a #gnutls_session_t structure. + * @list: is a 0 terminated list of gnutls_compression_method_t elements. + * + * Sets the priority on the compression algorithms supported by gnutls. + * Priority is higher for elements specified before others. + * After specifying the algorithms you want, you must append a 0. + * Note that the priority is set on the client. The server does + * not use the algorithm's priority except for disabling + * algorithms that were not specified. + * + * TLS 1.0 does not define any compression algorithms except + * NULL. Other compression algorithms are to be considered + * as gnutls extensions. + * + * Returns 0 on success. + * + **/ +int +gnutls_compression_set_priority (gnutls_session_t session, const int *list) +{ + return _set_priority (&session->internals.priorities.compression, list); +} + +/** + * gnutls_protocol_set_priority - Sets the priority on the protocol versions supported by gnutls. + * @session: is a #gnutls_session_t structure. + * @list: is a 0 terminated list of gnutls_protocol_t elements. + * + * Sets the priority on the protocol versions supported by gnutls. + * This function actually enables or disables protocols. Newer protocol + * versions always have highest priority. + * + * Returns 0 on success. + * + **/ +int +gnutls_protocol_set_priority (gnutls_session_t session, const int *list) +{ + int ret; + + ret = _set_priority (&session->internals.priorities.protocol, list); + + /* set the current version to the first in the chain. + * This will be overridden later. + */ + if (list) + _gnutls_set_current_version (session, list[0]); + + return ret; +} + +/** + * gnutls_certificate_type_set_priority - Sets the priority on the certificate types supported by gnutls. + * @session: is a #gnutls_session_t structure. + * @list: is a 0 terminated list of gnutls_certificate_type_t elements. + * + * Sets the priority on the certificate types supported by gnutls. + * Priority is higher for elements specified before others. + * After specifying the types you want, you must append a 0. + * Note that the certificate type priority is set on the client. + * The server does not use the cert type priority except for disabling + * types that were not specified. + * + * Returns 0 on success. + * + **/ +int +gnutls_certificate_type_set_priority (gnutls_session_t session, + const int *list) +{ +#ifdef ENABLE_OPENPGP + return _set_priority (&session->internals.priorities.cert_type, list); + +#else + + return GNUTLS_E_UNIMPLEMENTED_FEATURE; + +#endif +} + +static const int protocol_priority[] = { GNUTLS_TLS1_1, + GNUTLS_TLS1_0, + 0 +}; + +static const int cipher_priority_secure256[] = { GNUTLS_CIPHER_AES_256_CBC, + 0 +}; + +static const int kx_priority_secure[] = { GNUTLS_KX_RSA, + 0 +}; + +static const int mac_priority_secure[] = { GNUTLS_MAC_SHA1, + 0 +}; + +static int cert_type_priority[] = { GNUTLS_CRT_X509, + 0 +}; + +static const int comp_priority[] = { GNUTLS_COMP_NULL, + 0 +}; + +typedef void (rmadd_func) (priority_st * priority_list, int alg); + +/** + * gnutls_priority_set - Sets priorities for the cipher suites supported by gnutls. + * @session: is a #gnutls_session_t structure. + * @priority: is a #gnutls_priority_t structure. + * + * Sets the priorities to use on the ciphers, key exchange methods, + * macs and compression methods. + * + * On success 0 is returned. + * + **/ +int +gnutls_priority_set (gnutls_session_t session, gnutls_priority_t priority) +{ + if (priority == NULL) + { + gnutls_assert (); + return GNUTLS_E_NO_CIPHER_SUITES; + } + + memcpy (&session->internals.priorities, priority, + sizeof (struct gnutls_priority_st)); + + return 0; +} + +/** + * gnutls_priority_init - Sets priorities for the cipher suites supported by gnutls. + * @priority_cache: is a #gnutls_prioritity_t structure. + * @priorities: is a string describing priorities + * @err_pos: In case of an error this will have the position in the string the error occured + * + * Sets priorities for the ciphers, key exchange methods, macs and + * compression methods. This is to avoid using the + * gnutls_*_priority() functions. + * + * The #priorities option allows you to specify a semi-colon + * separated list of the cipher priorities to enable. + * + * Unless the first keyword is "NONE" the defaults are: + * Protocols: TLS1.1, TLS1.0, and SSL3.0. + * Compression: NULL. + * Certificate types: X.509, OpenPGP. + * + * You can also use predefined sets of ciphersuites: "PERFORMANCE" + * all the "secure" ciphersuites are enabled, limited to 128 bit + * ciphers and sorted by terms of speed performance. + * + * "NORMAL" option enables all "secure" ciphersuites. The 256-bit ciphers + * are included as a fallback only. The ciphers are sorted by security margin. + * + * "SECURE128" flag enables all "secure" ciphersuites with ciphers up to + * 128 bits, sorted by security margin. + * + * "SECURE256" flag enables all "secure" ciphersuites including the 256 bit + * ciphers, sorted by security margin. + * + * "EXPORT" all the ciphersuites are enabled, including the + * low-security 40 bit ciphers. + * + * "NONE" nothing is enabled. This disables even protocols and + * compression methods. + * + * Special keywords: + * '!' or '-' appended with an algorithm will remove this algorithm. + * '+' appended with an algorithm will add this algorithm. + * '%COMPAT' will enable compatibility features for a server. + * + * To avoid collisions in order to specify a compression algorithm in + * this string you have to prefix it with "COMP-", protocol versions + * with "VERS-" and certificate types with "CTYPE-". All other + * algorithms don't need a prefix. + * + * For key exchange algorithms when in NORMAL or SECURE levels the + * perfect forward secrecy algorithms take precendence of the other + * protocols. In all cases all the supported key exchange algorithms + * are enabled (except for the RSA-EXPORT which is only enabled in + * EXPORT level). + * + * Note that although one can select very long key sizes (such as 256 bits) + * for symmetric algorithms, to actually increase security the public key + * algorithms have to use longer key sizes as well. + * + * Examples: "NORMAL:!AES-128-CBC", + * "EXPORT:!VERS-TLS1.0:+COMP-DEFLATE:+CTYPE-OPENPGP", + * "NONE:+VERS-TLS1.0:+AES-128-CBC:+RSA:+SHA1:+COMP-NULL", "NORMAL", + * "NORMAL:%COMPAT". + * + * Returns: On syntax error GNUTLS_E_INVALID_REQUEST is returned and + * 0 on success. + **/ +int +gnutls_priority_init (gnutls_priority_t * priority_cache, + const char *priorities, const char **err_pos) +{ + int broken_list_size, i, j; + char *darg; + int algo; + + *priority_cache = gnutls_calloc (1, sizeof (struct gnutls_priority_st)); + if (*priority_cache == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + /* set mode to "SECURE256" */ + _set_priority (&(*priority_cache)->protocol, protocol_priority); + _set_priority (&(*priority_cache)->cipher, cipher_priority_secure256); + _set_priority (&(*priority_cache)->kx, kx_priority_secure); + _set_priority (&(*priority_cache)->mac, mac_priority_secure); + _set_priority (&(*priority_cache)->cert_type, cert_type_priority); + _set_priority (&(*priority_cache)->compression, comp_priority); + (*priority_cache)->no_padding = 0; + + return 0; +} + +/** + * gnutls_priority_deinit - Deinitialize the priorities cache for the cipher suites supported by gnutls. + * @priority_cache: is a #gnutls_prioritity_t structure. + * + * Deinitializes the priority cache. + * + **/ +void +gnutls_priority_deinit (gnutls_priority_t priority_cache) +{ + gnutls_free (priority_cache); +} + +/** + * gnutls_priority_set_direct - Sets priorities for the cipher suites supported by gnutls. + * @session: is a #gnutls_session_t structure. + * @priorities: is a string describing priorities + * @err_pos: In case of an error this will have the position in the string the error occured + * + * Sets the priorities to use on the ciphers, key exchange methods, + * macs and compression methods. This function avoids keeping a + * priority cache and is used to directly set string priorities to a + * TLS session. For documentation check the gnutls_priority_init(). + * + * On syntax error GNUTLS_E_INVALID_REQUEST is returned and 0 on success. + * + **/ +int +gnutls_priority_set_direct (gnutls_session_t session, + const char *priorities, const char **err_pos) +{ + gnutls_priority_t prio; + int ret; + + ret = gnutls_priority_init (&prio, priorities, err_pos); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + ret = gnutls_priority_set (session, prio); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + gnutls_priority_deinit (prio); + + return 0; +} + +/* Breaks a list of "xxx", "yyy", to a character array, of + * MAX_COMMA_SEP_ELEMENTS size; Note that the given string is modified. + */ +static void +break_comma_list (char *etag, + char **broken_etag, + int *elements, int max_elements, char sep) +{ + char *p = etag; + if (sep == 0) + sep = ','; + + *elements = 0; + + do + { + broken_etag[*elements] = p; + + (*elements)++; + + p = strchr (p, sep); + if (p) + { + *p = 0; + p++; /* move to next entry and skip white + * space. + */ + while (*p == ' ') + p++; + } + } + while (p != NULL && *elements < max_elements); +} + +/** + * gnutls_set_default_priority - Sets some default priority on the cipher suites supported by gnutls. + * @session: is a #gnutls_session_t structure. + * + * Sets some default priority on the ciphers, key exchange methods, + * macs and compression methods. + * + * This is the same as calling: + * + * gnutls_priority_set_direct (session, "NORMAL", NULL); + * + * This function is kept around for backwards compatibility, but + * because of its wide use it is still fully supported. If you wish + * to allow users to provide a string that specify which ciphers to + * use (which is recommended), you should use + * gnutls_priority_set_direct() or gnutls_priority_set() instead. + * + * Returns 0 on success. + **/ +int +gnutls_set_default_priority (gnutls_session_t session) +{ + return gnutls_priority_set_direct (session, "NORMAL", NULL); +} + +/** + * gnutls_set_default_export_priority - Sets some default priority on the cipher suites supported by gnutls. + * @session: is a #gnutls_session_t structure. + * + * Sets some default priority on the ciphers, key exchange methods, macs + * and compression methods. This function also includes weak algorithms. + * + * This is the same as calling: + * + * gnutls_priority_set_direct (session, "EXPORT", NULL); + * + * This function is kept around for backwards compatibility, but + * because of its wide use it is still fully supported. If you wish + * to allow users to provide a string that specify which ciphers to + * use (which is recommended), you should use + * gnutls_priority_set_direct() or gnutls_priority_set() instead. + * + * Returns 0 on success. + **/ +int +gnutls_set_default_export_priority (gnutls_session_t session) +{ + return gnutls_priority_set_direct (session, "EXPORT", NULL); +} diff --git a/src/daemon/https/tls/gnutls_record.c b/src/daemon/https/tls/gnutls_record.c new file mode 100644 index 00000000..cacbdecc --- /dev/null +++ b/src/daemon/https/tls/gnutls_record.c @@ -0,0 +1,1204 @@ +/* + * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +/* Functions that are record layer specific, are included in this file. + */ + +#include "gnutls_int.h" +#include "gnutls_errors.h" +#include "debug.h" +#include "gnutls_compress.h" +#include "gnutls_cipher.h" +#include "gnutls_buffers.h" +#include "gnutls_handshake.h" +#include "gnutls_hash_int.h" +#include "gnutls_cipher_int.h" +#include "gnutls_algorithms.h" +#include "gnutls_db.h" +#include "gnutls_auth_int.h" +#include "gnutls_num.h" +#include "gnutls_record.h" +#include "gnutls_datum.h" +#include "ext_max_record.h" +#include +#include + +/** + * gnutls_protocol_get_version - Returns the version of the currently used protocol + * @session: is a #gnutls_session_t structure. + * + * Returns: the version of the currently used protocol. + **/ +gnutls_protocol_t gnutls_protocol_get_version(gnutls_session_t session) +{ + return session->security_parameters.version; +} + +void _gnutls_set_current_version(gnutls_session_t session, + gnutls_protocol_t version) +{ + session->security_parameters.version = version; +} + +/** + * gnutls_transport_set_lowat - Used to set the lowat value in order for select to check for pending data. + * @session: is a #gnutls_session_t structure. + * @num: is the low water value. + * + * Used to set the lowat value in order for select to check if there + * are pending data to socket buffer. Used only if you have changed + * the default low water value (default is 1). Normally you will not + * need that function. This function is only useful if using + * berkeley style sockets. Otherwise it must be called and set lowat + * to zero. + **/ +void gnutls_transport_set_lowat(gnutls_session_t session, + int num) +{ + session->internals.lowat = num; +} + +/** + * gnutls_record_disable_padding - Used to disabled padding in TLS 1.0 and above + * @session: is a #gnutls_session_t structure. + * + * Used to disabled padding in TLS 1.0 and above. Normally you do + * not need to use this function, but there are buggy clients that + * complain if a server pads the encrypted data. This of course will + * disable protection against statistical attacks on the data. + * + * Normally only servers that require maximum compatibility with everything + * out there, need to call this function. + **/ +void gnutls_record_disable_padding(gnutls_session_t session) +{ + session->internals.priorities.no_padding = 1; +} + +/** + * gnutls_transport_set_ptr - Used to set first argument of the transport functions + * @session: is a #gnutls_session_t structure. + * @ptr: is the value. + * + * Used to set the first argument of the transport function (like + * PUSH and PULL). In berkeley style sockets this function will set + * the connection handle. + **/ +void gnutls_transport_set_ptr(gnutls_session_t session, + gnutls_transport_ptr_t ptr) +{ + session->internals.transport_recv_ptr = ptr; + session->internals.transport_send_ptr = ptr; +} + +/** + * gnutls_transport_set_ptr2 - Used to set first argument of the transport functions + * @session: is a #gnutls_session_t structure. + * @recv_ptr: is the value for the pull function + * @send_ptr: is the value for the push function + * + * Used to set the first argument of the transport function (like + * PUSH and PULL). In berkeley style sockets this function will set + * the connection handle. With this function you can use two + * different pointers for receiving and sending. + **/ +void gnutls_transport_set_ptr2(gnutls_session_t session, + gnutls_transport_ptr_t recv_ptr, + gnutls_transport_ptr_t send_ptr) +{ + session->internals.transport_send_ptr = send_ptr; + session->internals.transport_recv_ptr = recv_ptr; +} + +/** + * gnutls_transport_get_ptr - Used to return the first argument of the transport functions + * @session: is a #gnutls_session_t structure. + * + * Used to get the first argument of the transport function (like + * PUSH and PULL). This must have been set using + * gnutls_transport_set_ptr(). + * + * Returns: first argument of the transport function. + **/ +gnutls_transport_ptr_t gnutls_transport_get_ptr(gnutls_session_t session) +{ + return session->internals.transport_recv_ptr; +} + +/** + * gnutls_transport_get_ptr2 - Used to return the first argument of the transport functions + * @session: is a #gnutls_session_t structure. + * @recv_ptr: will hold the value for the pull function + * @send_ptr: will hold the value for the push function + * + * Used to get the arguments of the transport functions (like PUSH + * and PULL). These should have been set using + * gnutls_transport_set_ptr2(). + **/ +void gnutls_transport_get_ptr2(gnutls_session_t session, + gnutls_transport_ptr_t * recv_ptr, + gnutls_transport_ptr_t * send_ptr) +{ + + *recv_ptr = session->internals.transport_recv_ptr; + *send_ptr = session->internals.transport_send_ptr; +} + +/** + * gnutls_bye - This function terminates the current TLS/SSL connection. + * @session: is a #gnutls_session_t structure. + * @how: is an integer + * + * Terminates the current TLS/SSL connection. The connection should + * have been initiated using gnutls_handshake(). @how should be one + * of %GNUTLS_SHUT_RDWR, %GNUTLS_SHUT_WR. + * + * In case of %GNUTLS_SHUT_RDWR then the TLS connection gets + * terminated and further receives and sends will be disallowed. If + * the return value is zero you may continue using the connection. + * %GNUTLS_SHUT_RDWR actually sends an alert containing a close + * request and waits for the peer to reply with the same message. + * + * In case of %GNUTLS_SHUT_WR then the TLS connection gets terminated + * and further sends will be disallowed. In order to reuse the + * connection you should wait for an EOF from the peer. + * %GNUTLS_SHUT_WR sends an alert containing a close request. + * + * Note that not all implementations will properly terminate a TLS + * connection. Some of them, usually for performance reasons, will + * terminate only the underlying transport layer, thus causing a + * transmission error to the peer. This error cannot be + * distinguished from a malicious party prematurely terminating the + * session, thus this behavior is not recommended. + * + * This function may also return %GNUTLS_E_AGAIN or + * %GNUTLS_E_INTERRUPTED; cf. gnutls_record_get_direction(). + * + * Returns: %GNUTLS_E_SUCCESS on success, or an error code, see + * function documentation for entire semantics. + **/ +int gnutls_bye(gnutls_session_t session, + gnutls_close_request_t how) +{ + int ret = 0; + + switch (STATE) + { + case STATE0: + case STATE60: + ret = _gnutls_io_write_flush(session); + STATE = STATE60; + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + case STATE61: + ret = gnutls_alert_send(session, GNUTLS_AL_WARNING, GNUTLS_A_CLOSE_NOTIFY); + STATE = STATE61; + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + case STATE62: + STATE = STATE62; + if (how == GNUTLS_SHUT_RDWR) + { + do + { + _gnutls_io_clear_peeked_data(session); + ret = _gnutls_recv_int(session, GNUTLS_ALERT, -1, NULL, 0); + } while (ret == GNUTLS_E_GOT_APPLICATION_DATA); + + if (ret >= 0) + session->internals.may_not_read = 1; + + if (ret < 0) + { + gnutls_assert (); + return ret; + } + } + STATE = STATE62; + + break; + default: + gnutls_assert () + ; + return GNUTLS_E_INTERNAL_ERROR; + } + + STATE = STATE0; + + session->internals.may_not_write = 1; + return 0; +} + +inline static void session_invalidate(gnutls_session_t session) +{ + session->internals.valid_connection = VALID_FALSE; +} + +inline static void session_unresumable(gnutls_session_t session) +{ + session->internals.resumable = RESUME_FALSE; +} + +/* returns 0 if session is valid + */ +inline static int session_is_valid(gnutls_session_t session) +{ + if (session->internals.valid_connection == VALID_FALSE) + return GNUTLS_E_INVALID_SESSION; + + return 0; +} + +/* Copies the record version into the headers. The + * version must have 2 bytes at least. + */ +inline static void copy_record_version(gnutls_session_t session, + gnutls_handshake_description_t htype, + opaque version[2]) +{ + gnutls_protocol_t lver; + + if (htype != GNUTLS_HANDSHAKE_CLIENT_HELLO + || session->internals.default_record_version[0] == 0) + { + lver = gnutls_protocol_get_version(session); + + version[0] = _gnutls_version_get_major(lver); + version[1] = _gnutls_version_get_minor(lver); + } + else + { + version[0] = session->internals.default_record_version[0]; + version[1] = session->internals.default_record_version[1]; + } +} + +/* This function behaves exactly like write(). The only difference is + * that it accepts, the gnutls_session_t and the content_type_t of data to + * send (if called by the user the Content is specific) + * It is intended to transfer data, under the current session. + * + * Oct 30 2001: Removed capability to send data more than MAX_RECORD_SIZE. + * This makes the function much easier to read, and more error resistant + * (there were cases were the old function could mess everything up). + * --nmav + * + * This function may accept a NULL pointer for data, and 0 for size, if + * and only if the previous send was interrupted for some reason. + * + */ +ssize_t _gnutls_send_int(gnutls_session_t session, + content_type_t type, + gnutls_handshake_description_t htype, + const void *_data, + size_t sizeofdata) +{ + uint8_t *cipher; + int cipher_size; + int retval, ret; + int data2send_size; + uint8_t headers[5]; + const uint8_t *data = _data; + + /* Do not allow null pointer if the send buffer is empty. + * If the previous send was interrupted then a null pointer is + * ok, and means to resume. + */ + if (session->internals.record_send_buffer.length == 0 && (sizeofdata == 0 + && _data == NULL)) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + if (type != GNUTLS_ALERT) /* alert messages are sent anyway */ + if (session_is_valid(session) || session->internals.may_not_write != 0) + { + gnutls_assert (); + return GNUTLS_E_INVALID_SESSION; + } + + headers[0] = type; + + /* Use the default record version, if it is + * set. + */ + copy_record_version(session, htype, &headers[1]); + + _gnutls_record_log + ("REC[%x]: Sending Packet[%d] %s(%d) with length: %d\n", session, + (int) _gnutls_uint64touint32 (&session->connection_state. + write_sequence_number), + _gnutls_packet2str (type), type, sizeofdata); + + if (sizeofdata > MAX_RECORD_SEND_SIZE) + data2send_size = MAX_RECORD_SEND_SIZE; + else + data2send_size = sizeofdata; + + /* Only encrypt if we don't have data to send + * from the previous run. - probably interrupted. + */ + if (session->internals.record_send_buffer.length > 0) + { + ret = _gnutls_io_write_flush(session); + if (ret > 0) + cipher_size = ret; + else + cipher_size = 0; + + cipher = NULL; + + retval = session->internals.record_send_buffer_user_size; + } + else + { + /* now proceed to packet encryption + */ + cipher_size = data2send_size + MAX_RECORD_OVERHEAD;cipher = gnutls_malloc (cipher_size); + if (cipher == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + cipher_size = + _gnutls_encrypt (session, headers, RECORD_HEADER_SIZE, data, + data2send_size, cipher, cipher_size, type, + (session->internals.priorities.no_padding == + 0) ? 1 : 0); + if (cipher_size <= 0) + { + gnutls_assert (); + if (cipher_size == 0) + cipher_size = GNUTLS_E_ENCRYPTION_FAILED; + gnutls_free (cipher); + return cipher_size; /* error */ + } + + retval = data2send_size; + session->internals.record_send_buffer_user_size = data2send_size; + + /* increase sequence number + */ + if (_gnutls_uint64pp + (&session->connection_state.write_sequence_number) != 0) + { + session_invalidate (session); + gnutls_assert (); + gnutls_free (cipher); + return GNUTLS_E_RECORD_LIMIT_REACHED; + } + + ret = _gnutls_io_write_buffered (session, cipher, cipher_size); + gnutls_free (cipher); + } + + if (ret != cipher_size) + { + if (ret < 0 && gnutls_error_is_fatal (ret) == 0) + { + /* If we have sent any data then just return + * the error value. Do not invalidate the session. + */ + gnutls_assert (); + return ret; + } + + if (ret> 0) + { + gnutls_assert (); + ret = GNUTLS_E_INTERNAL_ERROR; + } + session_unresumable (session); + session->internals.may_not_write = 1; + gnutls_assert (); + return ret; + } + + session->internals.record_send_buffer_user_size = 0; + + _gnutls_record_log ("REC[%x]: Sent Packet[%d] %s(%d) with length: %d\n", + session, + (int) _gnutls_uint64touint32 (&session-> + connection_state. + write_sequence_number), + _gnutls_packet2str (type), type, cipher_size); + + return retval; +} + +/* This function is to be called if the handshake was successfully + * completed. This sends a Change Cipher Spec packet to the peer. + */ +ssize_t _gnutls_send_change_cipher_spec(gnutls_session_t session, + int again) +{ + static const opaque data[1] = + { + GNUTLS_TYPE_CHANGE_CIPHER_SPEC }; + + _gnutls_handshake_log ("REC[%x]: Sent ChangeCipherSpec\n", session); + + if (again == 0) + return _gnutls_send_int(session, GNUTLS_CHANGE_CIPHER_SPEC, -1, data, 1); + else + { + return _gnutls_io_write_flush(session); + } +} + +inline static int check_recv_type(content_type_t recv_type) +{ + switch (recv_type) + { + case GNUTLS_CHANGE_CIPHER_SPEC: + case GNUTLS_ALERT: + case GNUTLS_HANDSHAKE: + case GNUTLS_APPLICATION_DATA: + case GNUTLS_INNER_APPLICATION: + return 0; + default: + gnutls_assert () + ; + return GNUTLS_A_UNEXPECTED_MESSAGE; + } + +} + +/* Checks if there are pending data in the record buffers. If there are + * then it copies the data. + */ +static int check_buffers(gnutls_session_t session, + content_type_t type, + opaque * data, + int sizeofdata) +{ + if ((type == GNUTLS_APPLICATION_DATA || type == GNUTLS_HANDSHAKE || type + == GNUTLS_INNER_APPLICATION) && _gnutls_record_buffer_get_size(type, + session) + > 0) + { + int ret, ret2; + ret = _gnutls_record_buffer_get(type, session, data, sizeofdata); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + /* if the buffer just got empty */ + if (_gnutls_record_buffer_get_size(type, session) == 0) + { + if ((ret2 = _gnutls_io_clear_peeked_data(session)) < 0) + { + gnutls_assert (); + return ret2; + } + } + + return ret; + } + + return 0; +} + +/* Checks the record headers and returns the length, version and + * content type. + */ +static int record_check_headers(gnutls_session_t session, + uint8_t headers[RECORD_HEADER_SIZE], + content_type_t type, + gnutls_handshake_description_t htype, + /*output */content_type_t * recv_type, + opaque version[2], + uint16_t * length, + uint16_t * header_size) +{ + + /* Read the first two bytes to determine if this is a + * version 2 message + */ + + if (htype == GNUTLS_HANDSHAKE_CLIENT_HELLO && type == GNUTLS_HANDSHAKE + && headers[0] > 127) + { + + /* if msb set and expecting handshake message + * it should be SSL 2 hello + */ + version[0] = 3; /* assume SSL 3.0 */ + version[1] = 0; + + *length = (((headers[0] & 0x7f) << 8)) | headers[1]; + + /* SSL 2.0 headers */ + *header_size = 2; + *recv_type = GNUTLS_HANDSHAKE; /* we accept only v2 client hello + */ + + /* in order to assist the handshake protocol. + * V2 compatibility is a mess. + */ + session->internals.v2_hello = *length; + + _gnutls_record_log ("REC[%x]: V2 packet received. Length: %d\n", + session, *length); + + } + else + { + /* version 3.x */ + *recv_type = headers[0]; + version[0] = headers[1]; + version[1] = headers[2]; + + /* No DECR_LEN, since headers has enough size. + */ + *length = _gnutls_read_uint16(&headers[3]); + } + + return 0; +} + +/* Here we check if the advertized version is the one we + * negotiated in the handshake. + */ +inline static int record_check_version(gnutls_session_t session, + gnutls_handshake_description_t htype, + opaque version[2]) +{ + if (htype == GNUTLS_HANDSHAKE_CLIENT_HELLO) + { + /* Reject hello packets with major version higher than 3. + */ + if (version[0] > 3) + { + gnutls_assert (); + _gnutls_record_log + ("REC[%x]: INVALID VERSION PACKET: (%d) %d.%d\n", session, + htype, version[0], version[1]); + return GNUTLS_E_UNSUPPORTED_VERSION_PACKET; + } + } + else if (htype != GNUTLS_HANDSHAKE_SERVER_HELLO + && gnutls_protocol_get_version(session) + != _gnutls_version_get(version[0], version[1])) + { + /* Reject record packets that have a different version than the + * one negotiated. Note that this version is not protected by any + * mac. I don't really think that this check serves any purpose. + */ + gnutls_assert (); + _gnutls_record_log ("REC[%x]: INVALID VERSION PACKET: (%d) %d.%d\n", + session, htype, version[0], version[1]); + + return GNUTLS_E_UNSUPPORTED_VERSION_PACKET; + } + + return 0; +} + +/* This function will check if the received record type is + * the one we actually expect. + */ +static int record_check_type(gnutls_session_t session, + content_type_t recv_type, + content_type_t type, + gnutls_handshake_description_t htype, + opaque * data, + int data_size) +{ + + int ret; + + if ((recv_type == type) && (type == GNUTLS_APPLICATION_DATA || type + == GNUTLS_HANDSHAKE || type == GNUTLS_INNER_APPLICATION)) + { + _gnutls_record_buffer_put(type, session, (void *) data, data_size); + } + else + { + switch (recv_type) + { + case GNUTLS_ALERT: + + _gnutls_record_log + ("REC[%x]: Alert[%d|%d] - %s - was received\n", session, + data[0], data[1], gnutls_alert_get_name ((int) data[1])); + + session->internals.last_alert = data[1]; + + /* if close notify is received and + * the alert is not fatal + */ + if (data[1] == GNUTLS_A_CLOSE_NOTIFY && data[0] != GNUTLS_AL_FATAL) + { + /* If we have been expecting for an alert do + */ + session->internals.read_eof = 1; + return GNUTLS_E_INT_RET_0; /* EOF */ + } + else + { + + /* if the alert is FATAL or WARNING + * return the apropriate message + */ + + gnutls_assert (); + ret = GNUTLS_E_WARNING_ALERT_RECEIVED; + if (data[0] == GNUTLS_AL_FATAL) + { + session_unresumable(session); + session_invalidate(session); + ret = GNUTLS_E_FATAL_ALERT_RECEIVED; + } + + return ret; + } + break; + + case GNUTLS_CHANGE_CIPHER_SPEC: + /* this packet is now handled in the recv_int() + * function + */ + gnutls_assert () + ; + + return GNUTLS_E_UNEXPECTED_PACKET; + + case GNUTLS_APPLICATION_DATA: + /* even if data is unexpected put it into the buffer */ + if ((ret = _gnutls_record_buffer_put(recv_type, session, (void *) data, + data_size)) < 0) + { + gnutls_assert (); + return ret; + } + + /* the got_application data is only returned + * if expecting client hello (for rehandshake + * reasons). Otherwise it is an unexpected packet + */ + if (type == GNUTLS_ALERT || (htype == GNUTLS_HANDSHAKE_CLIENT_HELLO + && type == GNUTLS_HANDSHAKE)) + return GNUTLS_E_GOT_APPLICATION_DATA; + else + { + gnutls_assert (); + return GNUTLS_E_UNEXPECTED_PACKET; + } + + break; + case GNUTLS_HANDSHAKE: + /* This is legal if HELLO_REQUEST is received - and we are a client. + * If we are a server, a client may initiate a renegotiation at any time. + */ + if (session->security_parameters.entity == GNUTLS_SERVER) + { + gnutls_assert (); + return GNUTLS_E_REHANDSHAKE; + } + + /* If we are already in a handshake then a Hello + * Request is illegal. But here we don't really care + * since this message will never make it up here. + */ + + /* So we accept it */ + return _gnutls_recv_hello_request(session, data, data_size); + + break; + case GNUTLS_INNER_APPLICATION: + /* even if data is unexpected put it into the buffer */ + if ((ret = _gnutls_record_buffer_put(recv_type, session, (void *) data, + data_size)) < 0) + { + gnutls_assert (); + return ret; + } + gnutls_assert () + ; + return GNUTLS_E_UNEXPECTED_PACKET; + break; + default: + + _gnutls_record_log + ("REC[%x]: Received Unknown packet %d expecting %d\n", + session, recv_type, type); + + gnutls_assert () + ; + return GNUTLS_E_INTERNAL_ERROR; + } + } + + return 0; + +} + +/* This function will return the internal (per session) temporary + * recv buffer. If the buffer was not initialized before it will + * also initialize it. + */ +inline static int get_temp_recv_buffer(gnutls_session_t session, + gnutls_datum_t * tmp) +{ + size_t max_record_size; + + if (gnutls_compression_get(session) != GNUTLS_COMP_NULL) + max_record_size = MAX_RECORD_RECV_SIZE + EXTRA_COMP_SIZE; + else + max_record_size = MAX_RECORD_RECV_SIZE; + + /* We allocate MAX_RECORD_RECV_SIZE length + * because we cannot predict the output data by the record + * packet length (due to compression). + */ + + if (max_record_size > session->internals.recv_buffer.size + || session->internals.recv_buffer.data == NULL) + { + + /* Initialize the internal buffer. + */ + session->internals.recv_buffer.data + = gnutls_realloc(session->internals.recv_buffer.data, max_record_size); + + if (session->internals.recv_buffer.data == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + session->internals.recv_buffer.size = max_record_size; + } + + tmp->data = session->internals.recv_buffer.data; + tmp->size = session->internals.recv_buffer.size; + + return 0; +} + +#define MAX_EMPTY_PACKETS_SEQUENCE 4 + +/* This function behaves exactly like read(). The only difference is + * that it accepts the gnutls_session_t and the content_type_t of data to + * receive (if called by the user the Content is Userdata only) + * It is intended to receive data, under the current session. + * + * The gnutls_handshake_description_t was introduced to support SSL V2.0 client hellos. + */ +ssize_t _gnutls_recv_int(gnutls_session_t session, + content_type_t type, + gnutls_handshake_description_t htype, + opaque * data, + size_t sizeofdata) +{ + gnutls_datum_t tmp; + int decrypted_length; + opaque version[2]; + uint8_t *headers; + content_type_t recv_type; + uint16_t length; + uint8_t *ciphertext; + uint8_t *recv_data; + int ret, ret2; + uint16_t header_size; + int empty_packet = 0; + + if (type != GNUTLS_ALERT && (sizeofdata == 0 || data == NULL)) + { + return GNUTLS_E_INVALID_REQUEST; + } + + begin: + + if (empty_packet > MAX_EMPTY_PACKETS_SEQUENCE) + { + gnutls_assert (); + return GNUTLS_E_TOO_MANY_EMPTY_PACKETS; + } + + if (session->internals.read_eof != 0) + { + /* if we have already read an EOF + */ + return 0; + } + else if (session_is_valid(session) != 0 || session->internals.may_not_read + != 0) + { + gnutls_assert (); + return GNUTLS_E_INVALID_SESSION; + } + + /* If we have enough data in the cache do not bother receiving + * a new packet. (in order to flush the cache) + */ + ret = check_buffers(session, type, data, sizeofdata); + if (ret != 0) + return ret; + + /* default headers for TLS 1.0 + */ + header_size = RECORD_HEADER_SIZE; + + if ((ret = _gnutls_io_read_buffered(session, &headers, header_size, -1)) + != header_size) + { + if (ret < 0 && gnutls_error_is_fatal(ret) == 0) + return ret; + + session_invalidate(session); + if (type == GNUTLS_ALERT) + { + gnutls_assert (); + return 0; /* we were expecting close notify */ + } + session_unresumable(session); + gnutls_assert (); + return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; + } + + if ((ret = record_check_headers(session, headers, type, htype, &recv_type, + version, &length, &header_size)) < 0) + { + gnutls_assert (); + return ret; + } + + /* Here we check if the Type of the received packet is + * ok. + */ + if ((ret = check_recv_type(recv_type)) < 0) + { + gnutls_assert (); + return ret; + } + + /* Here we check if the advertized version is the one we + * negotiated in the handshake. + */ + if ((ret = record_check_version(session, htype, version)) < 0) + { + gnutls_assert (); + session_invalidate(session); + return ret; + } + + _gnutls_record_log + ("REC[%x]: Expected Packet[%d] %s(%d) with length: %d\n", session, + (int) _gnutls_uint64touint32 (&session->connection_state. + read_sequence_number), + _gnutls_packet2str (type), type, sizeofdata); + _gnutls_record_log + ("REC[%x]: Received Packet[%d] %s(%d) with length: %d\n", session, + (int) _gnutls_uint64touint32 (&session->connection_state. + read_sequence_number), + _gnutls_packet2str (recv_type), recv_type, length); + + if (length > MAX_RECV_SIZE) + { + _gnutls_record_log + ("REC[%x]: FATAL ERROR: Received packet with length: %d\n", + session, length); + + session_unresumable(session); + session_invalidate(session); + gnutls_assert (); + return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; + } + + /* check if we have that data into buffer. + */ + if ((ret = _gnutls_io_read_buffered(session, &recv_data, + header_size + length, recv_type)) + != header_size + length) + { + if (ret < 0 && gnutls_error_is_fatal(ret) == 0) + return ret; + + session_unresumable(session); + session_invalidate(session); + gnutls_assert (); + return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; + } + + /* ok now we are sure that we can read all the data - so + * move on ! + */ + _gnutls_io_clear_read_buffer(session); + ciphertext = &recv_data[header_size]; + + ret = get_temp_recv_buffer(session, &tmp); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + /* decrypt the data we got. + */ + ret = _gnutls_decrypt(session, ciphertext, length, tmp.data, tmp.size, + recv_type); + if (ret < 0) + { + session_unresumable(session); + session_invalidate(session); + gnutls_assert (); + return ret; + } + decrypted_length = ret; + + /* Check if this is a CHANGE_CIPHER_SPEC + */ + if (type == GNUTLS_CHANGE_CIPHER_SPEC && recv_type + == GNUTLS_CHANGE_CIPHER_SPEC) + { + + _gnutls_record_log + ("REC[%x]: ChangeCipherSpec Packet was received\n", session); + + if ((size_t) ret != sizeofdata) + { /* sizeofdata should be 1 */ + gnutls_assert (); + return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; + } + memcpy(data, tmp.data, sizeofdata); + + return ret; + } + + _gnutls_record_log + ("REC[%x]: Decrypted Packet[%d] %s(%d) with length: %d\n", session, + (int) _gnutls_uint64touint32 (&session->connection_state. + read_sequence_number), + _gnutls_packet2str (recv_type), recv_type, decrypted_length); + + /* increase sequence number + */ + if (_gnutls_uint64pp(&session->connection_state.read_sequence_number) != 0) + { + session_invalidate(session); + gnutls_assert (); + return GNUTLS_E_RECORD_LIMIT_REACHED; + } + + ret = record_check_type(session, recv_type, type, htype, tmp.data, + decrypted_length); + if (ret < 0) + { + if (ret == GNUTLS_E_INT_RET_0) + return 0; + gnutls_assert (); + return ret; + } + + /* Get Application data from buffer + */ + if ((recv_type == type) && (type == GNUTLS_APPLICATION_DATA || type + == GNUTLS_HANDSHAKE || type == GNUTLS_INNER_APPLICATION)) + { + + ret = _gnutls_record_buffer_get(type, session, data, sizeofdata); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + /* if the buffer just got empty + */ + if (_gnutls_record_buffer_get_size(type, session) == 0) + { + if ((ret2 = _gnutls_io_clear_peeked_data(session)) < 0) + { + gnutls_assert (); + return ret2; + } + } + } + else + { + gnutls_assert (); + return GNUTLS_E_UNEXPECTED_PACKET; + /* we didn't get what we wanted to + */ + } + + /* (originally for) TLS 1.0 CBC protection. + * Actually this code is called if we just received + * an empty packet. An empty TLS packet is usually + * sent to protect some vulnerabilities in the CBC mode. + * In that case we go to the beginning and start reading + * the next packet. + */ + if (ret == 0) + { + empty_packet++; + goto begin; + } + + return ret; +} + +/** + * gnutls_record_send - sends to the peer the specified data + * @session: is a #gnutls_session_t structure. + * @data: contains the data to send + * @sizeofdata: is the length of the data + * + * This function has the similar semantics with send(). The only + * difference is that is accepts a GNUTLS session, and uses different + * error codes. + * + * Note that if the send buffer is full, send() will block this + * function. See the send() documentation for full information. You + * can replace the default push function by using + * gnutls_transport_set_ptr2() with a call to send() with a + * MSG_DONTWAIT flag if blocking is a problem. + * + * If the EINTR is returned by the internal push function (the + * default is send()} then %GNUTLS_E_INTERRUPTED will be returned. If + * %GNUTLS_E_INTERRUPTED or %GNUTLS_E_AGAIN is returned, you must + * call this function again, with the same parameters; alternatively + * you could provide a %NULL pointer for data, and 0 for + * size. cf. gnutls_record_get_direction(). + * + * Returns: the number of bytes sent, or a negative error code. The + * number of bytes sent might be less than @sizeofdata. The maximum + * number of bytes this function can send in a single call depends on + * the negotiated maximum record size. + **/ +ssize_t gnutls_record_send(gnutls_session_t session, + const void *data, + size_t sizeofdata) +{ + return _gnutls_send_int(session, GNUTLS_APPLICATION_DATA, -1, data, + sizeofdata); +} + +/** + * gnutls_record_recv - reads data from the TLS record protocol + * @session: is a #gnutls_session_t structure. + * @data: the buffer that the data will be read into + * @sizeofdata: the number of requested bytes + * + * This function has the similar semantics with recv(). The only + * difference is that is accepts a GNUTLS session, and uses different + * error codes. + * + * In the special case that a server requests a renegotiation, the + * client may receive an error code of %GNUTLS_E_REHANDSHAKE. This + * message may be simply ignored, replied with an alert containing + * NO_RENEGOTIATION, or replied with a new handshake, depending on + * the client's will. + * + * If %EINTR is returned by the internal push function (the default + * is recv()) then %GNUTLS_E_INTERRUPTED will be returned. If + * %GNUTLS_E_INTERRUPTED or %GNUTLS_E_AGAIN is returned, you must + * call this function again to get the data. See also + * gnutls_record_get_direction(). + * + * A server may also receive %GNUTLS_E_REHANDSHAKE when a client has + * initiated a handshake. In that case the server can only initiate a + * handshake or terminate the connection. + * + * Returns: the number of bytes received and zero on EOF. A negative + * error code is returned in case of an error. The number of bytes + * received might be less than @sizeofdata. + **/ +ssize_t gnutls_record_recv(gnutls_session_t session, + void *data, + size_t sizeofdata) +{ + return _gnutls_recv_int(session, GNUTLS_APPLICATION_DATA, -1, data, + sizeofdata); +} + +/** + * gnutls_record_get_max_size - returns the maximum record size + * @session: is a #gnutls_session_t structure. + * + * This function returns the maximum record packet size in this + * connection. The maximum record size is negotiated by the client + * after the first handshake message. + **/ +size_t gnutls_record_get_max_size(gnutls_session_t session) +{ + /* Recv will hold the negotiated max record size + * always. + */ + return session->security_parameters.max_record_recv_size; +} + +/** + * gnutls_record_set_max_size - sets the maximum record size + * @session: is a #gnutls_session_t structure. + * @size: is the new size + * + * This function sets the maximum record packet size in this + * connection. This property can only be set to clients. The server + * may choose not to accept the requested size. + * + * Acceptable values are 512(=2^9), 1024(=2^10), 2048(=2^11) and + * 4096(=2^12). Returns 0 on success. The requested record size does + * get in effect immediately only while sending data. The receive + * part will take effect after a successful handshake. + * + * This function uses a TLS extension called 'max record size'. Not + * all TLS implementations use or even understand this extension. + **/ +ssize_t gnutls_record_set_max_size(gnutls_session_t session, + size_t size) +{ + ssize_t new_size; + + if (session->security_parameters.entity == GNUTLS_SERVER) + return GNUTLS_E_INVALID_REQUEST; + + new_size = _gnutls_mre_record2num(size); + + if (new_size < 0) + { + gnutls_assert (); + return new_size; + } + + session->security_parameters.max_record_send_size = size; + + session->internals.proposed_record_size = size; + + return 0; +} diff --git a/src/daemon/https/tls/gnutls_record.h b/src/daemon/https/tls/gnutls_record.h new file mode 100644 index 00000000..5595f32a --- /dev/null +++ b/src/daemon/https/tls/gnutls_record.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +ssize_t _gnutls_send_int (gnutls_session_t session, content_type_t type, + gnutls_handshake_description_t htype, + const void *data, size_t sizeofdata); +ssize_t _gnutls_recv_int (gnutls_session_t session, content_type_t type, + gnutls_handshake_description_t, opaque * data, + size_t sizeofdata); +ssize_t _gnutls_send_change_cipher_spec (gnutls_session_t session, int again); +void gnutls_transport_set_lowat (gnutls_session_t session, int num); diff --git a/src/daemon/https/tls/gnutls_rsa_export.c b/src/daemon/https/tls/gnutls_rsa_export.c new file mode 100644 index 00000000..66fad085 --- /dev/null +++ b/src/daemon/https/tls/gnutls_rsa_export.c @@ -0,0 +1,361 @@ +/* + * Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +/* This file contains code for RSA temporary keys. These keys are + * only used in export cipher suites. + */ + +#include +#include +#include +#include +#include "debug.h" +/* x509 */ +#include "x509.h" +#include "privkey.h" + +/* This function takes a number of bits and returns a supported + * number of bits. Ie a number of bits that we have a prime in the + * dh_primes structure. + */ + +#define MAX_SUPPORTED_BITS 512 + +/* returns e and m, depends on the requested bits. + * We only support limited key sizes. + */ +const mpi_t * +_gnutls_rsa_params_to_mpi (gnutls_rsa_params_t rsa_params) +{ + if (rsa_params == NULL) + { + return NULL; + } + + return rsa_params->params; + +} + +/* resarr will contain: modulus(0), public exponent(1), private exponent(2), + * prime1 - p (3), prime2 - q(4), u (5). + */ +int +_gnutls_rsa_generate_params (mpi_t * resarr, int *resarr_len, int bits) +{ + + int ret; + gcry_sexp_t parms, key, list; + + ret = gcry_sexp_build (&parms, NULL, "(genkey(rsa(nbits %d)))", bits); + if (ret != 0) + { + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + /* generate the RSA key */ + ret = gcry_pk_genkey (&key, parms); + gcry_sexp_release (parms); + + if (ret != 0) + { + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + list = gcry_sexp_find_token (key, "n", 0); + if (list == NULL) + { + gnutls_assert (); + gcry_sexp_release (key); + return GNUTLS_E_INTERNAL_ERROR; + } + + resarr[0] = gcry_sexp_nth_mpi (list, 1, 0); + gcry_sexp_release (list); + + list = gcry_sexp_find_token (key, "e", 0); + if (list == NULL) + { + gnutls_assert (); + gcry_sexp_release (key); + return GNUTLS_E_INTERNAL_ERROR; + } + + resarr[1] = gcry_sexp_nth_mpi (list, 1, 0); + gcry_sexp_release (list); + + list = gcry_sexp_find_token (key, "d", 0); + if (list == NULL) + { + gnutls_assert (); + gcry_sexp_release (key); + return GNUTLS_E_INTERNAL_ERROR; + } + + resarr[2] = gcry_sexp_nth_mpi (list, 1, 0); + gcry_sexp_release (list); + + list = gcry_sexp_find_token (key, "p", 0); + if (list == NULL) + { + gnutls_assert (); + gcry_sexp_release (key); + return GNUTLS_E_INTERNAL_ERROR; + } + + resarr[3] = gcry_sexp_nth_mpi (list, 1, 0); + gcry_sexp_release (list); + + + list = gcry_sexp_find_token (key, "q", 0); + if (list == NULL) + { + gnutls_assert (); + gcry_sexp_release (key); + return GNUTLS_E_INTERNAL_ERROR; + } + + resarr[4] = gcry_sexp_nth_mpi (list, 1, 0); + gcry_sexp_release (list); + + + list = gcry_sexp_find_token (key, "u", 0); + if (list == NULL) + { + gnutls_assert (); + gcry_sexp_release (key); + return GNUTLS_E_INTERNAL_ERROR; + } + + resarr[5] = gcry_sexp_nth_mpi (list, 1, 0); + gcry_sexp_release (list); + + gcry_sexp_release (key); + + _gnutls_dump_mpi ("n: ", resarr[0]); + _gnutls_dump_mpi ("e: ", resarr[1]); + _gnutls_dump_mpi ("d: ", resarr[2]); + _gnutls_dump_mpi ("p: ", resarr[3]); + _gnutls_dump_mpi ("q: ", resarr[4]); + _gnutls_dump_mpi ("u: ", resarr[5]); + + *resarr_len = 6; + + return 0; + +} + + +/** + * gnutls_rsa_params_import_raw - This function will replace the old RSA parameters + * @rsa_params: Is a structure will hold the parameters + * @m: holds the modulus + * @e: holds the public exponent + * @d: holds the private exponent + * @p: holds the first prime (p) + * @q: holds the second prime (q) + * @u: holds the coefficient + * + * This function will replace the parameters in the given structure. + * The new parameters should be stored in the appropriate gnutls_datum. + * + **/ +int +gnutls_rsa_params_import_raw (gnutls_rsa_params_t rsa_params, + const gnutls_datum_t * m, + const gnutls_datum_t * e, + const gnutls_datum_t * d, + const gnutls_datum_t * p, + const gnutls_datum_t * q, + const gnutls_datum_t * u) +{ + return gnutls_x509_privkey_import_rsa_raw (rsa_params, m, e, d, p, q, u); +} + +/** + * gnutls_rsa_params_init - This function will initialize the temporary RSA parameters + * @rsa_params: Is a structure that will hold the parameters + * + * This function will initialize the temporary RSA parameters structure. + * + **/ +int +gnutls_rsa_params_init (gnutls_rsa_params_t * rsa_params) +{ + int ret; + + ret = gnutls_x509_privkey_init (rsa_params); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + (*rsa_params)->crippled = 1; + + return 0; +} + +/** + * gnutls_rsa_params_deinit - This function will deinitialize the RSA parameters + * @rsa_params: Is a structure that holds the parameters + * + * This function will deinitialize the RSA parameters structure. + * + **/ +void +gnutls_rsa_params_deinit (gnutls_rsa_params_t rsa_params) +{ + gnutls_x509_privkey_deinit (rsa_params); +} + +/** + * gnutls_rsa_params_cpy - This function will copy an RSA parameters structure + * @dst: Is the destination structure, which should be initialized. + * @src: Is the source structure + * + * This function will copy the RSA parameters structure from source + * to destination. + * + **/ +int +gnutls_rsa_params_cpy (gnutls_rsa_params_t dst, gnutls_rsa_params_t src) +{ + return gnutls_x509_privkey_cpy (dst, src); +} + +/** + * gnutls_rsa_params_generate2 - This function will generate temporary RSA parameters + * @params: The structure where the parameters will be stored + * @bits: is the prime's number of bits + * + * This function will generate new temporary RSA parameters for use in + * RSA-EXPORT ciphersuites. This function is normally slow. + * + * Note that if the parameters are to be used in export cipher suites the + * bits value should be 512 or less. + * Also note that the generation of new RSA parameters is only useful + * to servers. Clients use the parameters sent by the server, thus it's + * no use calling this in client side. + * + **/ +int +gnutls_rsa_params_generate2 (gnutls_rsa_params_t params, unsigned int bits) +{ + return gnutls_x509_privkey_generate (params, GNUTLS_PK_RSA, bits, 0); +} + +/** + * gnutls_rsa_params_import_pkcs1 - This function will import RSA params from a pkcs1 structure + * @params: A structure where the parameters will be copied to + * @pkcs1_params: should contain a PKCS1 RSAPublicKey structure PEM or DER encoded + * @format: the format of params. PEM or DER. + * + * This function will extract the RSAPublicKey found in a PKCS1 formatted + * structure. + * + * If the structure is PEM encoded, it should have a header + * of "BEGIN RSA PRIVATE KEY". + * + * In case of failure a negative value will be returned, and + * 0 on success. + * + **/ +int +gnutls_rsa_params_import_pkcs1 (gnutls_rsa_params_t params, + const gnutls_datum_t * pkcs1_params, + gnutls_x509_crt_fmt_t format) +{ + return gnutls_x509_privkey_import (params, pkcs1_params, format); +} + + +/** + * gnutls_rsa_params_export_pkcs1 - This function will export RSA params to a pkcs1 structure + * @params: Holds the RSA parameters + * @format: the format of output params. One of PEM or DER. + * @params_data: will contain a PKCS1 RSAPublicKey structure PEM or DER encoded + * @params_data_size: holds the size of params_data (and will be replaced by the actual size of parameters) + * + * This function will export the given RSA parameters to a PKCS1 + * RSAPublicKey structure. If the buffer provided is not long enough to + * hold the output, then GNUTLS_E_SHORT_MEMORY_BUFFER will be returned. + * + * If the structure is PEM encoded, it will have a header + * of "BEGIN RSA PRIVATE KEY". + * + * In case of failure a negative value will be returned, and + * 0 on success. + * + **/ +int +gnutls_rsa_params_export_pkcs1 (gnutls_rsa_params_t params, + gnutls_x509_crt_fmt_t format, + unsigned char *params_data, + size_t * params_data_size) +{ + return gnutls_x509_privkey_export (params, format, + params_data, params_data_size); +} + + +/** + * gnutls_rsa_params_export_raw - This function will export the RSA parameters + * @params: a structure that holds the rsa parameters + * @m: will hold the modulus + * @e: will hold the public exponent + * @d: will hold the private exponent + * @p: will hold the first prime (p) + * @q: will hold the second prime (q) + * @u: will hold the coefficient + * @bits: if non null will hold the prime's number of bits + * + * This function will export the RSA parameters found in the given + * structure. The new parameters will be allocated using + * gnutls_malloc() and will be stored in the appropriate datum. + * + **/ +int +gnutls_rsa_params_export_raw (gnutls_rsa_params_t params, + gnutls_datum_t * m, gnutls_datum_t * e, + gnutls_datum_t * d, gnutls_datum_t * p, + gnutls_datum_t * q, gnutls_datum_t * u, + unsigned int *bits) +{ + int ret; + + ret = gnutls_x509_privkey_export_rsa_raw (params, m, e, d, p, q, u); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + if (bits) + *bits = _gnutls_mpi_get_nbits (params->params[3]); + + return 0; + +} diff --git a/src/daemon/https/tls/gnutls_rsa_export.h b/src/daemon/https/tls/gnutls_rsa_export.h new file mode 100644 index 00000000..b39e5e93 --- /dev/null +++ b/src/daemon/https/tls/gnutls_rsa_export.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +const mpi_t *_gnutls_rsa_params_to_mpi (gnutls_rsa_params_t); +int _gnutls_peers_cert_less_512 (gnutls_session_t session); +int _gnutls_rsa_generate_params (mpi_t * resarr, int *resarr_len, int bits); diff --git a/src/daemon/https/tls/gnutls_session.c b/src/daemon/https/tls/gnutls_session.c new file mode 100644 index 00000000..541cc699 --- /dev/null +++ b/src/daemon/https/tls/gnutls_session.c @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2000, 2003, 2004, 2005, 2007 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ +#include "gnutls_int.h" +#include "gnutls_errors.h" +#include "debug.h" +#include +#include + +/** + * gnutls_session_get_data - Returns all session parameters. + * @session: is a #gnutls_session_t structure. + * @session_data: is a pointer to space to hold the session. + * @session_data_size: is the session_data's size, or it will be set by the function. + * + * Returns all session parameters, in order to support resuming. + * The client should call this, and keep the returned session, if he wants to + * resume that current version later by calling gnutls_session_set_data() + * This function must be called after a successful handshake. + * + * Resuming sessions is really useful and speedups connections after a succesful one. + **/ +int +gnutls_session_get_data (gnutls_session_t session, + void *session_data, size_t * session_data_size) +{ + + gnutls_datum_t psession; + int ret; + + if (session->internals.resumable == RESUME_FALSE) + return GNUTLS_E_INVALID_SESSION; + + psession.data = session_data; + + ret = _gnutls_session_pack (session, &psession); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + *session_data_size = psession.size; + + if (psession.size > *session_data_size) + { + ret = GNUTLS_E_SHORT_MEMORY_BUFFER; + goto error; + } + + if (session_data != NULL) + memcpy (session_data, psession.data, psession.size); + + ret = 0; + +error: + _gnutls_free_datum (&psession); + return ret; +} + +/** + * gnutls_session_get_data2 - Returns all session parameters. + * @session: is a #gnutls_session_t structure. + * @session_data: is a pointer to a datum that will hold the session. + * + * Returns all session parameters, in order to support resuming. + * The client should call this, and keep the returned session, if he wants to + * resume that current version later by calling gnutls_session_set_data() + * This function must be called after a successful handshake. The returned + * datum must be freed with gnutls_free(). + * + * Resuming sessions is really useful and speedups connections after a succesful one. + **/ +int +gnutls_session_get_data2 (gnutls_session_t session, gnutls_datum_t * data) +{ + + int ret; + + if (data == NULL) + { + return GNUTLS_E_INVALID_REQUEST; + } + + if (session->internals.resumable == RESUME_FALSE) + return GNUTLS_E_INVALID_SESSION; + + ret = _gnutls_session_pack (session, data); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + return 0; +} + + +/** + * gnutls_session_get_id - Returns session id. + * @session: is a #gnutls_session_t structure. + * @session_id: is a pointer to space to hold the session id. + * @session_id_size: is the session id's size, or it will be set by the function. + * + * Returns the current session id. This can be used if you want to check if + * the next session you tried to resume was actually resumed. + * This is because resumed sessions have the same sessionID with the + * original session. + * + * Session id is some data set by the server, that identify the current session. + * In TLS 1.0 and SSL 3.0 session id is always less than 32 bytes. + * + * Returns zero on success. + **/ +int +gnutls_session_get_id (gnutls_session_t session, + void *session_id, size_t * session_id_size) +{ + size_t given_session_id_size = *session_id_size; + + *session_id_size = session->security_parameters.session_id_size; + + /* just return the session size */ + if (session_id == NULL) + { + return 0; + } + + if (given_session_id_size < session->security_parameters.session_id_size) + { + return GNUTLS_E_SHORT_MEMORY_BUFFER; + } + + memcpy (session_id, &session->security_parameters.session_id, + *session_id_size); + + return 0; +} + +/** + * gnutls_session_set_data - Sets all session parameters + * @session: is a #gnutls_session_t structure. + * @session_data: is a pointer to space to hold the session. + * @session_data_size: is the session's size + * + * Sets all session parameters, in order to resume a previously established + * session. The session data given must be the one returned by gnutls_session_get_data(). + * This function should be called before gnutls_handshake(). + * + * Keep in mind that session resuming is advisory. The server may + * choose not to resume the session, thus a full handshake will be + * performed. + * + * Returns a negative value on error. + * + **/ +int +gnutls_session_set_data (gnutls_session_t session, + const void *session_data, size_t session_data_size) +{ + int ret; + gnutls_datum_t psession; + + psession.data = (opaque *) session_data; + psession.size = session_data_size; + + if (session_data == NULL || session_data_size == 0) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + ret = _gnutls_session_unpack (session, &psession); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + return 0; +} diff --git a/src/daemon/https/tls/gnutls_session.h b/src/daemon/https/tls/gnutls_session.h new file mode 100644 index 00000000..dae99edc --- /dev/null +++ b/src/daemon/https/tls/gnutls_session.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ diff --git a/src/daemon/https/tls/gnutls_session_pack.c b/src/daemon/https/tls/gnutls_session_pack.c new file mode 100644 index 00000000..f18fe97a --- /dev/null +++ b/src/daemon/https/tls/gnutls_session_pack.c @@ -0,0 +1,1204 @@ +/* + * Copyright (C) 2000, 2004, 2005, 2007 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +/* Contains functions that are supposed to pack and unpack session data, + * before and after they are sent to the database backend. + */ + +#include +#ifdef ENABLE_SRP +# include +#endif +#ifdef ENABLE_PSK +# include +#endif +#include +#include +#include +#include +#include +#include +#include + +#define PACK_HEADER_SIZE 1 +#define MAX_SEC_PARAMS 7+MAX_SRP_USERNAME+MAX_SERVER_NAME_EXTENSIONS*(3+MAX_SERVER_NAME_SIZE)+165 +static int pack_certificate_auth_info (gnutls_session_t, + gnutls_datum_t * packed_session); +static int unpack_certificate_auth_info (gnutls_session_t, + const gnutls_datum_t * + packed_session); + +static int unpack_srp_auth_info (gnutls_session_t session, + const gnutls_datum_t * packed_session); +static int pack_srp_auth_info (gnutls_session_t session, + gnutls_datum_t * packed_session); + +static int unpack_psk_auth_info (gnutls_session_t session, + const gnutls_datum_t * packed_session); +static int pack_psk_auth_info (gnutls_session_t session, + gnutls_datum_t * packed_session); + +static int unpack_anon_auth_info (gnutls_session_t session, + const gnutls_datum_t * packed_session); +static int pack_anon_auth_info (gnutls_session_t session, + gnutls_datum_t * packed_session); + +static int unpack_security_parameters (gnutls_session_t session, + const gnutls_datum_t * packed_session); +static int pack_security_parameters (gnutls_session_t session, + gnutls_datum_t * packed_session); + + +/* Since auth_info structures contain malloced data, this function + * is required in order to pack these structures in a vector in + * order to store them to the DB. + * + * packed_session will contain the session data. + * + * The data will be in a platform independent format. + */ +int +_gnutls_session_pack (gnutls_session_t session, + gnutls_datum_t * packed_session) +{ + int ret; + + if (packed_session == NULL) + { + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + + switch (gnutls_auth_get_type (session)) + { +#ifdef ENABLE_SRP + case GNUTLS_CRD_SRP: + ret = pack_srp_auth_info (session, packed_session); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + break; +#endif +#ifdef ENABLE_PSK + case GNUTLS_CRD_PSK: + ret = pack_psk_auth_info (session, packed_session); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + break; +#endif +#ifdef ENABLE_ANON + case GNUTLS_CRD_ANON: + ret = pack_anon_auth_info (session, packed_session); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + break; +#endif + case GNUTLS_CRD_CERTIFICATE: + ret = pack_certificate_auth_info (session, packed_session); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + break; + default: + return GNUTLS_E_INTERNAL_ERROR; + + } + + /* Auth_info structures copied. Now copy security_parameters_st. + * packed_session must have allocated space for the security parameters. + */ + ret = pack_security_parameters (session, packed_session); + if (ret < 0) + { + gnutls_assert (); + _gnutls_free_datum (packed_session); + return ret; + } + + return 0; +} + + +/* Load session data from a buffer. + */ +int +_gnutls_session_unpack (gnutls_session_t session, + const gnutls_datum_t * packed_session) +{ + int ret; + + if (packed_session == NULL || packed_session->size == 0) + { + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + if (_gnutls_get_auth_info (session) != NULL) + { + _gnutls_free_auth_info (session); + } + + switch (packed_session->data[0]) + { +#ifdef ENABLE_SRP + case GNUTLS_CRD_SRP: + ret = unpack_srp_auth_info (session, packed_session); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + break; +#endif +#ifdef ENABLE_PSK + case GNUTLS_CRD_PSK: + ret = unpack_psk_auth_info (session, packed_session); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + break; +#endif +#ifdef ENABLE_ANON + case GNUTLS_CRD_ANON: + ret = unpack_anon_auth_info (session, packed_session); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + break; +#endif + case GNUTLS_CRD_CERTIFICATE: + ret = unpack_certificate_auth_info (session, packed_session); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + break; + default: + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + + } + + /* Auth_info structures copied. Now copy security_parameters_st. + * packed_session must have allocated space for the security parameters. + */ + ret = unpack_security_parameters (session, packed_session); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + return 0; +} + + +/* Format: + * 1 byte the credentials type + * 4 bytes the size of the whole structure + * DH stuff + * 2 bytes the size of secret key in bits + * 4 bytes the size of the prime + * x bytes the prime + * 4 bytes the size of the generator + * x bytes the generator + * 4 bytes the size of the public key + * x bytes the public key + * RSA stuff + * 4 bytes the size of the modulus + * x bytes the modulus + * 4 bytes the size of the exponent + * x bytes the exponent + * CERTIFICATES + * 4 bytes the length of the certificate list + * 4 bytes the size of first certificate + * x bytes the certificate + * and so on... + */ +static int +pack_certificate_auth_info (gnutls_session_t session, + gnutls_datum_t * packed_session) +{ + unsigned int pos = 0, i; + int cert_size, pack_size; + cert_auth_info_t info = _gnutls_get_auth_info (session); + + if (info) + { + cert_size = 4; + + for (i = 0; i < info->ncerts; i++) + cert_size += 4 + info->raw_certificate_list[i].size; + + pack_size = 2 + 4 + info->dh.prime.size + + 4 + info->dh.generator.size + 4 + info->dh.public_key.size + + 4 + info->rsa_export.modulus.size + + 4 + info->rsa_export.exponent.size + cert_size; + } + else + pack_size = 0; + + packed_session->size = PACK_HEADER_SIZE + pack_size + sizeof (uint32_t); + + /* calculate the size and allocate the data. + */ + packed_session->data = + gnutls_malloc (packed_session->size + MAX_SEC_PARAMS); + + if (packed_session->data == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + packed_session->data[0] = GNUTLS_CRD_CERTIFICATE; + _gnutls_write_uint32 (pack_size, &packed_session->data[PACK_HEADER_SIZE]); + pos += 4 + PACK_HEADER_SIZE; + + + if (pack_size > 0) + { + + _gnutls_write_uint16 (info->dh.secret_bits, &packed_session->data[pos]); + pos += 2; + + _gnutls_write_datum32 (&packed_session->data[pos], info->dh.prime); + pos += 4 + info->dh.prime.size; + _gnutls_write_datum32 (&packed_session->data[pos], info->dh.generator); + pos += 4 + info->dh.generator.size; + _gnutls_write_datum32 (&packed_session->data[pos], info->dh.public_key); + pos += 4 + info->dh.public_key.size; + + _gnutls_write_datum32 (&packed_session->data[pos], + info->rsa_export.modulus); + pos += 4 + info->rsa_export.modulus.size; + _gnutls_write_datum32 (&packed_session->data[pos], + info->rsa_export.exponent); + pos += 4 + info->rsa_export.exponent.size; + + _gnutls_write_uint32 (info->ncerts, &packed_session->data[pos]); + pos += 4; + + for (i = 0; i < info->ncerts; i++) + { + _gnutls_write_datum32 (&packed_session->data[pos], + info->raw_certificate_list[i]); + pos += sizeof (uint32_t) + info->raw_certificate_list[i].size; + } + } + + return 0; +} + + +/* Upack certificate info. + */ +static int +unpack_certificate_auth_info (gnutls_session_t session, + const gnutls_datum_t * packed_session) +{ + int pos = 0, size, ret; + unsigned int i = 0, j; + size_t pack_size; + cert_auth_info_t info; + + if (packed_session->data[0] != GNUTLS_CRD_CERTIFICATE) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + pack_size = _gnutls_read_uint32 (&packed_session->data[PACK_HEADER_SIZE]); + pos += PACK_HEADER_SIZE + 4; + + if (pack_size == 0) + return 0; /* nothing to be done */ + + /* a simple check for integrity */ + if (pack_size + PACK_HEADER_SIZE + 4 > packed_session->size) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + /* client and server have the same auth_info here + */ + ret = + _gnutls_auth_info_set (session, GNUTLS_CRD_CERTIFICATE, + sizeof (cert_auth_info_st), 1); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + info = _gnutls_get_auth_info (session); + if (info == NULL) + { + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + info->dh.secret_bits = _gnutls_read_uint16 (&packed_session->data[pos]); + pos += 2; + + size = _gnutls_read_uint32 (&packed_session->data[pos]); + pos += 4; + ret = _gnutls_set_datum (&info->dh.prime, &packed_session->data[pos], size); + if (ret < 0) + { + gnutls_assert (); + goto error; + } + pos += size; + + size = _gnutls_read_uint32 (&packed_session->data[pos]); + pos += 4; + ret = + _gnutls_set_datum (&info->dh.generator, &packed_session->data[pos], size); + if (ret < 0) + { + gnutls_assert (); + goto error; + } + pos += size; + + size = _gnutls_read_uint32 (&packed_session->data[pos]); + pos += 4; + ret = + _gnutls_set_datum (&info->dh.public_key, &packed_session->data[pos], + size); + if (ret < 0) + { + gnutls_assert (); + goto error; + } + pos += size; + + size = _gnutls_read_uint32 (&packed_session->data[pos]); + pos += 4; + ret = + _gnutls_set_datum (&info->rsa_export.modulus, + &packed_session->data[pos], size); + if (ret < 0) + { + gnutls_assert (); + goto error; + } + pos += size; + + size = _gnutls_read_uint32 (&packed_session->data[pos]); + pos += 4; + ret = + _gnutls_set_datum (&info->rsa_export.exponent, + &packed_session->data[pos], size); + if (ret < 0) + { + gnutls_assert (); + goto error; + } + pos += size; + + info->ncerts = _gnutls_read_uint32 (&packed_session->data[pos]); + pos += 4; + + if (info->ncerts > 0) + { + info->raw_certificate_list = + gnutls_calloc (1, sizeof (gnutls_datum_t) * info->ncerts); + if (info->raw_certificate_list == NULL) + { + gnutls_assert (); + ret = GNUTLS_E_MEMORY_ERROR; + goto error; + } + } + + for (i = 0; i < info->ncerts; i++) + { + size = _gnutls_read_uint32 (&packed_session->data[pos]); + pos += sizeof (uint32_t); + + ret = + _gnutls_set_datum (&info->raw_certificate_list[i], + &packed_session->data[pos], size); + pos += size; + + if (ret < 0) + { + gnutls_assert (); + goto error; + } + } + + + return 0; + +error: + _gnutls_free_datum (&info->dh.prime); + _gnutls_free_datum (&info->dh.generator); + _gnutls_free_datum (&info->dh.public_key); + + _gnutls_free_datum (&info->rsa_export.modulus); + _gnutls_free_datum (&info->rsa_export.exponent); + + for (j = 0; j < i; j++) + _gnutls_free_datum (&info->raw_certificate_list[j]); + + gnutls_free (info->raw_certificate_list); + + return ret; + +} + +#ifdef ENABLE_SRP +/* Packs the SRP session authentication data. + */ + +/* Format: + * 1 byte the credentials type + * 4 bytes the size of the SRP username (x) + * x bytes the SRP username + */ +static int +pack_srp_auth_info (gnutls_session_t session, gnutls_datum_t * packed_session) +{ + srp_server_auth_info_t info = _gnutls_get_auth_info (session); + int pack_size; + + if (info && info->username) + pack_size = strlen (info->username) + 1; /* include the terminating null */ + else + pack_size = 0; + + packed_session->size = PACK_HEADER_SIZE + pack_size + sizeof (uint32_t); + + /* calculate the size and allocate the data. + */ + packed_session->data = + gnutls_malloc (packed_session->size + MAX_SEC_PARAMS); + + if (packed_session->data == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + packed_session->data[0] = GNUTLS_CRD_SRP; + _gnutls_write_uint32 (pack_size, &packed_session->data[PACK_HEADER_SIZE]); + + if (pack_size > 0) + memcpy (&packed_session->data[PACK_HEADER_SIZE + sizeof (uint32_t)], + info->username, pack_size + 1); + + return 0; +} + + +static int +unpack_srp_auth_info (gnutls_session_t session, + const gnutls_datum_t * packed_session) +{ + size_t username_size; + int ret; + srp_server_auth_info_t info; + + if (packed_session->data[0] != GNUTLS_CRD_SRP) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + username_size = + _gnutls_read_uint32 (&packed_session->data[PACK_HEADER_SIZE]); + + if (username_size == 0) + return 0; /* nothing to be done */ + + /* a simple check for integrity */ + if (username_size + 4 + PACK_HEADER_SIZE > packed_session->size) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = + _gnutls_auth_info_set (session, GNUTLS_CRD_SRP, + sizeof (srp_server_auth_info_st), 1); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + info = _gnutls_get_auth_info (session); + if (info == NULL) + { + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + memcpy (info->username, + &packed_session->data[PACK_HEADER_SIZE + sizeof (uint32_t)], + username_size); + + return 0; +} +#endif + + +#ifdef ENABLE_ANON +/* Packs the ANON session authentication data. + */ + +/* Format: + * 1 byte the credentials type + * 4 bytes the size of the whole structure + * 2 bytes the size of secret key in bits + * 4 bytes the size of the prime + * x bytes the prime + * 4 bytes the size of the generator + * x bytes the generator + * 4 bytes the size of the public key + * x bytes the public key + */ +static int +pack_anon_auth_info (gnutls_session_t session, + gnutls_datum_t * packed_session) +{ + anon_auth_info_t info = _gnutls_get_auth_info (session); + int pos = 0; + size_t pack_size; + + if (info) + pack_size = 2 + 4 * 3 + info->dh.prime.size + + info->dh.generator.size + info->dh.public_key.size; + else + pack_size = 0; + + packed_session->size = PACK_HEADER_SIZE + pack_size + sizeof (uint32_t); + + /* calculate the size and allocate the data. + */ + packed_session->data = + gnutls_malloc (packed_session->size + MAX_SEC_PARAMS); + + if (packed_session->data == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + packed_session->data[0] = GNUTLS_CRD_ANON; + _gnutls_write_uint32 (pack_size, &packed_session->data[PACK_HEADER_SIZE]); + pos += 4 + PACK_HEADER_SIZE; + + if (pack_size > 0) + { + _gnutls_write_uint16 (info->dh.secret_bits, &packed_session->data[pos]); + pos += 2; + + _gnutls_write_datum32 (&packed_session->data[pos], info->dh.prime); + pos += 4 + info->dh.prime.size; + _gnutls_write_datum32 (&packed_session->data[pos], info->dh.generator); + pos += 4 + info->dh.generator.size; + _gnutls_write_datum32 (&packed_session->data[pos], info->dh.public_key); + pos += 4 + info->dh.public_key.size; + + } + + return 0; +} + + +static int +unpack_anon_auth_info (gnutls_session_t session, + const gnutls_datum_t * packed_session) +{ + size_t pack_size; + int pos = 0, size, ret; + anon_auth_info_t info; + + if (packed_session->data[0] != GNUTLS_CRD_ANON) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + pack_size = _gnutls_read_uint32 (&packed_session->data[PACK_HEADER_SIZE]); + pos += PACK_HEADER_SIZE + 4; + + + if (pack_size == 0) + return 0; /* nothing to be done */ + + /* a simple check for integrity */ + if (pack_size + PACK_HEADER_SIZE + 4 > packed_session->size) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + /* client and serer have the same auth_info here + */ + ret = + _gnutls_auth_info_set (session, GNUTLS_CRD_ANON, + sizeof (anon_auth_info_st), 1); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + info = _gnutls_get_auth_info (session); + if (info == NULL) + { + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + info->dh.secret_bits = _gnutls_read_uint16 (&packed_session->data[pos]); + pos += 2; + + size = _gnutls_read_uint32 (&packed_session->data[pos]); + pos += 4; + ret = _gnutls_set_datum (&info->dh.prime, &packed_session->data[pos], size); + if (ret < 0) + { + gnutls_assert (); + goto error; + } + pos += size; + + size = _gnutls_read_uint32 (&packed_session->data[pos]); + pos += 4; + ret = + _gnutls_set_datum (&info->dh.generator, &packed_session->data[pos], size); + if (ret < 0) + { + gnutls_assert (); + goto error; + } + pos += size; + + size = _gnutls_read_uint32 (&packed_session->data[pos]); + pos += 4; + ret = + _gnutls_set_datum (&info->dh.public_key, &packed_session->data[pos], + size); + if (ret < 0) + { + gnutls_assert (); + goto error; + } + pos += size; + + return 0; + +error: + _gnutls_free_datum (&info->dh.prime); + _gnutls_free_datum (&info->dh.generator); + _gnutls_free_datum (&info->dh.public_key); + return ret; +} +#endif /* ANON */ + +#ifdef ENABLE_PSK +/* Packs the PSK session authentication data. + */ + +/* Format: + * 1 byte the credentials type + * 4 bytes the size of the whole structure + * 4 bytes the size of the PSK username (x) + * x bytes the PSK username + * 2 bytes the size of secret key in bits + * 4 bytes the size of the prime + * x bytes the prime + * 4 bytes the size of the generator + * x bytes the generator + * 4 bytes the size of the public key + * x bytes the public key + */ +static int +pack_psk_auth_info (gnutls_session_t session, gnutls_datum_t * packed_session) +{ + psk_auth_info_t info; + int pack_size, username_size = 0, pos; + + info = _gnutls_get_auth_info (session); + + if (info) + { + username_size = strlen (info->username) + 1; /* include the terminating null */ + pack_size = username_size + + 2 + 4 * 3 + info->dh.prime.size + info->dh.generator.size + + info->dh.public_key.size; + } + else + pack_size = 0; + + packed_session->size = PACK_HEADER_SIZE + pack_size + sizeof (uint32_t); + + /* calculate the size and allocate the data. + */ + packed_session->data = + gnutls_malloc (packed_session->size + MAX_SEC_PARAMS); + + if (packed_session->data == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + pos = 0; + + packed_session->data[pos] = GNUTLS_CRD_PSK; + pos++; + + _gnutls_write_uint32 (pack_size, &packed_session->data[pos]); + pos += 4; + + + if (pack_size > 0) + { + _gnutls_write_uint32 (username_size, &packed_session->data[pos]); + pos += 4; + + memcpy (&packed_session->data[pos], info->username, username_size); + pos += username_size; + + _gnutls_write_uint16 (info->dh.secret_bits, &packed_session->data[pos]); + pos += 2; + + _gnutls_write_datum32 (&packed_session->data[pos], info->dh.prime); + pos += 4 + info->dh.prime.size; + _gnutls_write_datum32 (&packed_session->data[pos], info->dh.generator); + pos += 4 + info->dh.generator.size; + _gnutls_write_datum32 (&packed_session->data[pos], info->dh.public_key); + pos += 4 + info->dh.public_key.size; + + } + + + return 0; +} + +static int +unpack_psk_auth_info (gnutls_session_t session, + const gnutls_datum_t * packed_session) +{ + size_t username_size; + size_t pack_size; + int pos = 0, size, ret; + psk_auth_info_t info; + + if (packed_session->data[0] != GNUTLS_CRD_PSK) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + pack_size = _gnutls_read_uint32 (&packed_session->data[PACK_HEADER_SIZE]); + pos += PACK_HEADER_SIZE + 4; + + + if (pack_size == 0) + return 0; /* nothing to be done */ + + /* a simple check for integrity */ + if (pack_size + PACK_HEADER_SIZE + 4 > packed_session->size) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + /* client and serer have the same auth_info here + */ + ret = + _gnutls_auth_info_set (session, GNUTLS_CRD_PSK, + sizeof (psk_auth_info_st), 1); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + info = _gnutls_get_auth_info (session); + if (info == NULL) + { + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + username_size = _gnutls_read_uint32 (&packed_session->data[pos]); + pos += 4; + + memcpy (info->username, &packed_session->data[pos], username_size); + pos += username_size; + + info->dh.secret_bits = _gnutls_read_uint16 (&packed_session->data[pos]); + pos += 2; + + size = _gnutls_read_uint32 (&packed_session->data[pos]); + pos += 4; + ret = _gnutls_set_datum (&info->dh.prime, &packed_session->data[pos], size); + if (ret < 0) + { + gnutls_assert (); + goto error; + } + pos += size; + + size = _gnutls_read_uint32 (&packed_session->data[pos]); + pos += 4; + ret = + _gnutls_set_datum (&info->dh.generator, &packed_session->data[pos], size); + if (ret < 0) + { + gnutls_assert (); + goto error; + } + pos += size; + + size = _gnutls_read_uint32 (&packed_session->data[pos]); + pos += 4; + ret = + _gnutls_set_datum (&info->dh.public_key, &packed_session->data[pos], + size); + if (ret < 0) + { + gnutls_assert (); + goto error; + } + pos += size; + + return 0; + +error: + _gnutls_free_datum (&info->dh.prime); + _gnutls_free_datum (&info->dh.generator); + _gnutls_free_datum (&info->dh.public_key); + return ret; +} +#endif + + +/* Packs the security parameters. + */ + +/* Format: + * 4 bytes the total security data size + * 1 byte the entity type (client/server) + * 1 byte the key exchange algorithm used + * 1 byte the read cipher algorithm + * 1 byte the read mac algorithm + * 1 byte the read compression algorithm + * + * 1 byte the write cipher algorithm + * 1 byte the write mac algorithm + * 1 byte the write compression algorithm + * + * 1 byte the certificate type + * 1 byte the protocol version + * + * 2 bytes the cipher suite + * + * 48 bytes the master secret + * + * 32 bytes the client random + * 32 bytes the server random + * + * 1 byte the session ID size + * x bytes the session ID (32 bytes max) + * + * 4 bytes a timestamp + * ------------------- + * MAX: 165 bytes + * + * EXTENSIONS: + * 2 bytes the record send size + * 2 bytes the record recv size + * + * 1 byte the SRP username size + * x bytes the SRP username (MAX_SRP_USERNAME) + * + * 2 bytes the number of server name extensions (up to MAX_SERVER_NAME_EXTENSIONS) + * 1 byte the first name type + * 2 bytes the size of the first name + * x bytes the first name (MAX_SERVER_NAME_SIZE) + * and so on... + * + * -------------------- + * MAX: 7+MAX_SRP_USERNAME+MAX_SERVER_NAME_EXTENSIONS*(3+MAX_SERVER_NAME_SIZE) + */ +static int +pack_security_parameters (gnutls_session_t session, + gnutls_datum_t * packed_session) +{ + int pos = 0; + size_t len, init, i; + + /* move after the auth info stuff. + */ + init = + _gnutls_read_uint32 (&packed_session->data[PACK_HEADER_SIZE]) + 4 + + PACK_HEADER_SIZE; + + pos = init + 4; /* make some space to write later the size */ + + packed_session->data[pos++] = session->security_parameters.entity; + packed_session->data[pos++] = session->security_parameters.kx_algorithm; + packed_session->data[pos++] = + session->security_parameters.read_bulk_cipher_algorithm; + packed_session->data[pos++] = + session->security_parameters.read_mac_algorithm; + packed_session->data[pos++] = + session->security_parameters.read_compression_algorithm; + packed_session->data[pos++] = + session->security_parameters.write_bulk_cipher_algorithm; + packed_session->data[pos++] = + session->security_parameters.write_mac_algorithm; + packed_session->data[pos++] = + session->security_parameters.write_compression_algorithm; + packed_session->data[pos++] = + session->security_parameters.current_cipher_suite.suite[0]; + packed_session->data[pos++] = + session->security_parameters.current_cipher_suite.suite[1]; + + packed_session->data[pos++] = session->security_parameters.cert_type; + packed_session->data[pos++] = session->security_parameters.version; + + memcpy (&packed_session->data[pos], + session->security_parameters.master_secret, TLS_MASTER_SIZE); + pos += TLS_MASTER_SIZE; + + memcpy (&packed_session->data[pos], + session->security_parameters.client_random, TLS_RANDOM_SIZE); + pos += TLS_RANDOM_SIZE; + memcpy (&packed_session->data[pos], + session->security_parameters.server_random, TLS_RANDOM_SIZE); + pos += TLS_RANDOM_SIZE; + + packed_session->data[pos++] = session->security_parameters.session_id_size; + memcpy (&packed_session->data[pos], session->security_parameters.session_id, + session->security_parameters.session_id_size); + pos += session->security_parameters.session_id_size; + + _gnutls_write_uint32 (session->security_parameters.timestamp, + &packed_session->data[pos]); + pos += 4; + + /* Extensions */ + _gnutls_write_uint16 (session->security_parameters.max_record_send_size, + &packed_session->data[pos]); + pos += 2; + + _gnutls_write_uint16 (session->security_parameters.max_record_recv_size, + &packed_session->data[pos]); + pos += 2; + + /* SRP */ + len = + strlen ((char *) session->security_parameters.extensions.srp_username); + packed_session->data[pos++] = len; + memcpy (&packed_session->data[pos], + session->security_parameters.extensions.srp_username, len); + pos += len; + + _gnutls_write_uint16 (session->security_parameters.extensions. + server_names_size, &packed_session->data[pos]); + pos += 2; + + for (i = 0; i < session->security_parameters.extensions.server_names_size; + i++) + { + packed_session->data[pos++] = + session->security_parameters.extensions.server_names[i].type; + _gnutls_write_uint16 (session->security_parameters.extensions. + server_names[i].name_length, + &packed_session->data[pos]); + pos += 2; + + memcpy (&packed_session->data[pos], + session->security_parameters.extensions.server_names[i].name, + session->security_parameters.extensions.server_names[i]. + name_length); + pos += + session->security_parameters.extensions.server_names[i].name_length; + } + + /* write the total size */ + _gnutls_write_uint32 (pos - init - 4, &packed_session->data[init]); + packed_session->size += pos - init; + + return 0; +} + + +static int +unpack_security_parameters (gnutls_session_t session, + const gnutls_datum_t * packed_session) +{ + size_t pack_size, init, i; + int pos = 0, len; + time_t timestamp = time (0); + + + /* skip the auth info stuff */ + init = + _gnutls_read_uint32 (&packed_session->data[PACK_HEADER_SIZE]) + 4 + + PACK_HEADER_SIZE; + + pos = init; + + pack_size = _gnutls_read_uint32 (&packed_session->data[pos]); + pos += 4; + + + if (pack_size == 0) + return GNUTLS_E_INVALID_REQUEST; + + /* a simple check for integrity */ + if (pack_size > MAX_SEC_PARAMS) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + session->internals.resumed_security_parameters.entity = + packed_session->data[pos++]; + session->internals.resumed_security_parameters.kx_algorithm = + packed_session->data[pos++]; + session->internals.resumed_security_parameters.read_bulk_cipher_algorithm = + packed_session->data[pos++]; + session->internals.resumed_security_parameters.read_mac_algorithm = + packed_session->data[pos++]; + session->internals.resumed_security_parameters.read_compression_algorithm = + packed_session->data[pos++]; + session->internals.resumed_security_parameters.write_bulk_cipher_algorithm = + packed_session->data[pos++]; + session->internals.resumed_security_parameters.write_mac_algorithm = + packed_session->data[pos++]; + session->internals.resumed_security_parameters.write_compression_algorithm = + packed_session->data[pos++]; + session->internals.resumed_security_parameters.current_cipher_suite. + suite[0] = packed_session->data[pos++]; + session->internals.resumed_security_parameters.current_cipher_suite. + suite[1] = packed_session->data[pos++]; + + session->internals.resumed_security_parameters.cert_type = + packed_session->data[pos++]; + session->internals.resumed_security_parameters.version = + packed_session->data[pos++]; + + memcpy (session->internals.resumed_security_parameters.master_secret, + &packed_session->data[pos], TLS_MASTER_SIZE); + pos += TLS_MASTER_SIZE; + + memcpy (session->internals.resumed_security_parameters.client_random, + &packed_session->data[pos], TLS_RANDOM_SIZE); + pos += TLS_RANDOM_SIZE; + memcpy (session->internals.resumed_security_parameters.server_random, + &packed_session->data[pos], TLS_RANDOM_SIZE); + pos += TLS_RANDOM_SIZE; + + session->internals.resumed_security_parameters.session_id_size = + packed_session->data[pos++]; + memcpy (session->internals.resumed_security_parameters.session_id, + &packed_session->data[pos], + session->internals.resumed_security_parameters.session_id_size); + pos += session->internals.resumed_security_parameters.session_id_size; + + session->internals.resumed_security_parameters.timestamp = + _gnutls_read_uint32 (&packed_session->data[pos]); + pos += 4; + + if (timestamp - session->internals.resumed_security_parameters.timestamp > + session->internals.expire_time + || session->internals.resumed_security_parameters.timestamp > timestamp) + { + gnutls_assert (); + return GNUTLS_E_EXPIRED; + } + + /* Extensions */ + session->internals.resumed_security_parameters.max_record_send_size = + _gnutls_read_uint16 (&packed_session->data[pos]); + pos += 2; + + session->internals.resumed_security_parameters.max_record_recv_size = + _gnutls_read_uint16 (&packed_session->data[pos]); + pos += 2; + + + /* SRP */ + len = packed_session->data[pos++]; /* srp username length */ + memcpy (session->internals.resumed_security_parameters.extensions. + srp_username, &packed_session->data[pos], len); + session->internals.resumed_security_parameters.extensions. + srp_username[len] = 0; + pos += len; + + session->internals.resumed_security_parameters.extensions. + server_names_size = _gnutls_read_uint16 (&packed_session->data[pos]); + pos += 2; + for (i = 0; + i < + session->internals.resumed_security_parameters.extensions. + server_names_size; i++) + { + session->internals.resumed_security_parameters.extensions. + server_names[i].type = packed_session->data[pos++]; + session->internals.resumed_security_parameters.extensions. + server_names[i].name_length = + _gnutls_read_uint16 (&packed_session->data[pos]); + pos += 2; + + memcpy (session->internals.resumed_security_parameters.extensions. + server_names[i].name, &packed_session->data[pos], + session->internals.resumed_security_parameters.extensions. + server_names[i].name_length); + pos += + session->internals.resumed_security_parameters.extensions. + server_names[i].name_length; + } + return 0; +} diff --git a/src/daemon/https/tls/gnutls_session_pack.h b/src/daemon/https/tls/gnutls_session_pack.h new file mode 100644 index 00000000..93dd32de --- /dev/null +++ b/src/daemon/https/tls/gnutls_session_pack.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +int _gnutls_session_pack (gnutls_session_t session, + gnutls_datum_t * packed_session); +int _gnutls_session_unpack (gnutls_session_t session, + const gnutls_datum_t * packed_session); diff --git a/src/daemon/https/tls/gnutls_sig.c b/src/daemon/https/tls/gnutls_sig.c new file mode 100644 index 00000000..0456438f --- /dev/null +++ b/src/daemon/https/tls/gnutls_sig.c @@ -0,0 +1,473 @@ +/* + * Copyright (C) 2001, 2004, 2005, 2006, 2007 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int _gnutls_tls_sign (gnutls_session_t session, + gnutls_cert * cert, + gnutls_privkey * pkey, + const gnutls_datum_t * hash_concat, + gnutls_datum_t * signature); + +/* Generates a signature of all the previous sent packets in the + * handshake procedure. (20040227: now it works for SSL 3.0 as well) + */ +int +_gnutls_tls_sign_hdata (gnutls_session_t session, + gnutls_cert * cert, + gnutls_privkey * pkey, gnutls_datum_t * signature) +{ + gnutls_datum_t dconcat; + int ret; + opaque concat[36]; + mac_hd_t td_md5; + mac_hd_t td_sha; + gnutls_protocol_t ver = gnutls_protocol_get_version (session); + + td_sha = _gnutls_hash_copy (session->internals.handshake_mac_handle_sha); + if (td_sha == NULL) + { + gnutls_assert (); + return GNUTLS_E_HASH_FAILED; + } + + if (ver == GNUTLS_SSL3) + { + ret = _gnutls_generate_master (session, 1); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + _gnutls_mac_deinit_ssl3_handshake (td_sha, &concat[16], + session->security_parameters. + master_secret, TLS_MASTER_SIZE); + } + else + _gnutls_hash_deinit (td_sha, &concat[16]); + + switch (cert->subject_pk_algorithm) + { + case GNUTLS_PK_RSA: + td_md5 = + _gnutls_hash_copy (session->internals.handshake_mac_handle_md5); + if (td_md5 == NULL) + { + gnutls_assert (); + return GNUTLS_E_HASH_FAILED; + } + + if (ver == GNUTLS_SSL3) + _gnutls_mac_deinit_ssl3_handshake (td_md5, concat, + session->security_parameters. + master_secret, TLS_MASTER_SIZE); + else + _gnutls_hash_deinit (td_md5, concat); + + dconcat.data = concat; + dconcat.size = 36; + break; + default: + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + ret = _gnutls_tls_sign (session, cert, pkey, &dconcat, signature); + if (ret < 0) + { + gnutls_assert (); + } + + return ret; +} + +/* Generates a signature of all the random data and the parameters. + * Used in DHE_* ciphersuites. + */ +int +_gnutls_tls_sign_params (gnutls_session_t session, + gnutls_cert * cert, + gnutls_privkey * pkey, + gnutls_datum_t * params, gnutls_datum_t * signature) +{ + gnutls_datum_t dconcat; + int ret; + mac_hd_t td_sha; + opaque concat[36]; + gnutls_protocol_t ver = gnutls_protocol_get_version (session); + + td_sha = _gnutls_hash_init (GNUTLS_MAC_SHA1); + if (td_sha == NULL) + { + gnutls_assert (); + return GNUTLS_E_HASH_FAILED; + } + + _gnutls_hash (td_sha, session->security_parameters.client_random, + TLS_RANDOM_SIZE); + _gnutls_hash (td_sha, session->security_parameters.server_random, + TLS_RANDOM_SIZE); + _gnutls_hash (td_sha, params->data, params->size); + + switch (cert->subject_pk_algorithm) + { + case GNUTLS_PK_RSA: + if (ver < GNUTLS_TLS1_2) + { + mac_hd_t td_md5 = _gnutls_hash_init (GNUTLS_MAC_MD5); + if (td_md5 == NULL) + { + gnutls_assert (); + return GNUTLS_E_HASH_FAILED; + } + + _gnutls_hash (td_md5, session->security_parameters.client_random, + TLS_RANDOM_SIZE); + _gnutls_hash (td_md5, session->security_parameters.server_random, + TLS_RANDOM_SIZE); + _gnutls_hash (td_md5, params->data, params->size); + + _gnutls_hash_deinit (td_md5, concat); + _gnutls_hash_deinit (td_sha, &concat[16]); + + dconcat.size = 36; + } + else + { +#if 1 + /* Use NULL parameters. */ + memcpy (concat, + "\x30\x21\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00\x04\x14", + 15); + _gnutls_hash_deinit (td_sha, &concat[15]); + dconcat.size = 35; +#else + /* No parameters field. */ + memcpy (concat, + "\x30\x1f\x30\x07\x06\x05\x2b\x0e\x03\x02\x1a\x04\x14", 13); + _gnutls_hash_deinit (td_sha, &concat[13]); + dconcat.size = 33; +#endif + } + dconcat.data = concat; + break; + default: + gnutls_assert (); + _gnutls_hash_deinit (td_sha, NULL); + return GNUTLS_E_INTERNAL_ERROR; + } + ret = _gnutls_tls_sign (session, cert, pkey, &dconcat, signature); + if (ret < 0) + { + gnutls_assert (); + } + + return ret; + +} + +/* This will create a PKCS1 or DSA signature, using the given parameters, and the + * given data. The output will be allocated and be put in signature. + */ +int +_gnutls_sign (gnutls_pk_algorithm_t algo, + mpi_t * params, + int params_size, + const gnutls_datum_t * data, gnutls_datum_t * signature) +{ + int ret; + + switch (algo) + { + case GNUTLS_PK_RSA: + /* encrypt */ + if ((ret = + _gnutls_pkcs1_rsa_encrypt (signature, data, params, params_size, + 1)) < 0) + { + gnutls_assert (); + return ret; + } + + break; + default: + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + break; + } + + return 0; +} + +/* This will create a PKCS1 or DSA signature, as defined in the TLS protocol. + * Cert is the certificate of the corresponding private key. It is only checked if + * it supports signing. + */ +static int +_gnutls_tls_sign (gnutls_session_t session, + gnutls_cert * cert, + gnutls_privkey * pkey, + const gnutls_datum_t * hash_concat, + gnutls_datum_t * signature) +{ + + /* If our certificate supports signing + */ + + if (cert != NULL) + if (cert->key_usage != 0) + if (!(cert->key_usage & KEY_DIGITAL_SIGNATURE)) + { + gnutls_assert (); + return GNUTLS_E_KEY_USAGE_VIOLATION; + } + + /* External signing. */ + if (!pkey || pkey->params_size == 0) + { + if (!session->internals.sign_func) + return GNUTLS_E_INSUFFICIENT_CREDENTIALS; + + return (*session->internals.sign_func) (session, + session->internals. + sign_func_userdata, + cert->cert_type, &cert->raw, + hash_concat, signature); + } + + return _gnutls_sign (pkey->pk_algorithm, pkey->params, pkey->params_size, + hash_concat, signature); +} + +static int +_gnutls_verify_sig (gnutls_cert * cert, + const gnutls_datum_t * hash_concat, + gnutls_datum_t * signature, size_t sha1pos) +{ + int ret; + gnutls_datum_t vdata; + + if (cert->version == 0 || cert == NULL) + { /* this is the only way to check + * if it is initialized + */ + gnutls_assert (); + return GNUTLS_E_CERTIFICATE_ERROR; + } + + /* If the certificate supports signing continue. + */ + if (cert != NULL) + if (cert->key_usage != 0) + if (!(cert->key_usage & KEY_DIGITAL_SIGNATURE)) + { + gnutls_assert (); + return GNUTLS_E_KEY_USAGE_VIOLATION; + } + + switch (cert->subject_pk_algorithm) + { + case GNUTLS_PK_RSA: + + vdata.data = hash_concat->data; + vdata.size = hash_concat->size; + + /* verify signature */ + if ((ret = _gnutls_rsa_verify (&vdata, signature, cert->params, + cert->params_size, 1)) < 0) + { + gnutls_assert (); + return ret; + } + + break; + default: + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + return 0; +} + +/* Verifies a TLS signature (like the one in the client certificate + * verify message). + */ +int +_gnutls_verify_sig_hdata (gnutls_session_t session, + gnutls_cert * cert, gnutls_datum_t * signature) +{ + int ret; + opaque concat[36]; + mac_hd_t td_md5; + mac_hd_t td_sha; + gnutls_datum_t dconcat; + gnutls_protocol_t ver = gnutls_protocol_get_version (session); + + td_md5 = _gnutls_hash_copy (session->internals.handshake_mac_handle_md5); + if (td_md5 == NULL) + { + gnutls_assert (); + return GNUTLS_E_HASH_FAILED; + } + + td_sha = _gnutls_hash_copy (session->internals.handshake_mac_handle_sha); + if (td_sha == NULL) + { + gnutls_assert (); + _gnutls_hash_deinit (td_md5, NULL); + return GNUTLS_E_HASH_FAILED; + } + + if (ver == GNUTLS_SSL3) + { + ret = _gnutls_generate_master (session, 1); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + _gnutls_mac_deinit_ssl3_handshake (td_md5, concat, + session->security_parameters. + master_secret, TLS_MASTER_SIZE); + _gnutls_mac_deinit_ssl3_handshake (td_sha, &concat[16], + session->security_parameters. + master_secret, TLS_MASTER_SIZE); + } + else + { + _gnutls_hash_deinit (td_md5, concat); + _gnutls_hash_deinit (td_sha, &concat[16]); + } + + dconcat.data = concat; + dconcat.size = 20 + 16; /* md5+ sha */ + + ret = _gnutls_verify_sig (cert, &dconcat, signature, 16); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + return ret; + +} + +/* Generates a signature of all the random data and the parameters. + * Used in DHE_* ciphersuites. + */ +int +_gnutls_verify_sig_params (gnutls_session_t session, + gnutls_cert * cert, + const gnutls_datum_t * params, + gnutls_datum_t * signature) +{ + gnutls_datum_t dconcat; + int ret; + mac_hd_t td_md5 = NULL; + mac_hd_t td_sha; + opaque concat[36]; + gnutls_protocol_t ver = gnutls_protocol_get_version (session); + + if (ver < GNUTLS_TLS1_2) + { + td_md5 = _gnutls_hash_init (GNUTLS_MAC_MD5); + if (td_md5 == NULL) + { + gnutls_assert (); + return GNUTLS_E_HASH_FAILED; + } + + _gnutls_hash (td_md5, session->security_parameters.client_random, + TLS_RANDOM_SIZE); + _gnutls_hash (td_md5, session->security_parameters.server_random, + TLS_RANDOM_SIZE); + _gnutls_hash (td_md5, params->data, params->size); + } + + td_sha = _gnutls_hash_init (GNUTLS_MAC_SHA1); + if (td_sha == NULL) + { + gnutls_assert (); + if (td_md5) + _gnutls_hash_deinit (td_md5, NULL); + return GNUTLS_E_HASH_FAILED; + } + + _gnutls_hash (td_sha, session->security_parameters.client_random, + TLS_RANDOM_SIZE); + _gnutls_hash (td_sha, session->security_parameters.server_random, + TLS_RANDOM_SIZE); + _gnutls_hash (td_sha, params->data, params->size); + + if (ver < GNUTLS_TLS1_2) + { + _gnutls_hash_deinit (td_md5, concat); + _gnutls_hash_deinit (td_sha, &concat[16]); + dconcat.size = 36; + } + else + { +#if 1 + /* Use NULL parameters. */ + memcpy (concat, + "\x30\x21\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00\x04\x14", + 15); + _gnutls_hash_deinit (td_sha, &concat[15]); + dconcat.size = 35; +#else + /* No parameters field. */ + memcpy (concat, + "\x30\x1f\x30\x07\x06\x05\x2b\x0e\x03\x02\x1a\x04\x14", 13); + _gnutls_hash_deinit (td_sha, &concat[13]); + dconcat.size = 33; +#endif + } + + dconcat.data = concat; + + ret = _gnutls_verify_sig (cert, &dconcat, signature, dconcat.size - 20); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + return ret; + +} diff --git a/src/daemon/https/tls/gnutls_sig.h b/src/daemon/https/tls/gnutls_sig.h new file mode 100644 index 00000000..4d770716 --- /dev/null +++ b/src/daemon/https/tls/gnutls_sig.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +#ifndef GNUTLS_SIG_H +# define GNUTLS_SIG_H + +int _gnutls_tls_sign_hdata (gnutls_session_t session, + gnutls_cert * cert, + gnutls_privkey * pkey, + gnutls_datum_t * signature); + +int _gnutls_tls_sign_params (gnutls_session_t session, + gnutls_cert * cert, + gnutls_privkey * pkey, + gnutls_datum_t * params, + gnutls_datum_t * signature); + +int _gnutls_verify_sig_hdata (gnutls_session_t session, + gnutls_cert * cert, gnutls_datum_t * signature); + +int _gnutls_verify_sig_params (gnutls_session_t session, + gnutls_cert * cert, + const gnutls_datum_t * params, + gnutls_datum_t * signature); + +int _gnutls_sign (gnutls_pk_algorithm_t algo, + mpi_t * params, int params_size, + const gnutls_datum_t * data, gnutls_datum_t * signature); + +#endif diff --git a/src/daemon/https/tls/gnutls_state.c b/src/daemon/https/tls/gnutls_state.c new file mode 100644 index 00000000..ccc865a1 --- /dev/null +++ b/src/daemon/https/tls/gnutls_state.c @@ -0,0 +1,1219 @@ +/* + * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +/* Functions to manipulate the session (gnutls_int.h), and some other stuff + * are included here. The file's name is traditionally gnutls_state even if the + * state has been renamed to session. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void +_gnutls_session_cert_type_set (gnutls_session_t session, + gnutls_certificate_type_t ct) +{ + session->security_parameters.cert_type = ct; +} + +/** + * gnutls_cipher_get - Returns the currently used cipher. + * @session: is a #gnutls_session_t structure. + * + * Returns: the currently used cipher. + **/ +gnutls_cipher_algorithm_t +gnutls_cipher_get (gnutls_session_t session) +{ + return session->security_parameters.read_bulk_cipher_algorithm; +} + +/** + * gnutls_certificate_type_get - Returns the currently used certificate type. + * @session: is a #gnutls_session_t structure. + * + * The certificate type is by default X.509, unless it is negotiated + * as a TLS extension. + * + * Returns: the currently used %gnutls_certificate_type_t certificate + * type. + **/ +gnutls_certificate_type_t +gnutls_certificate_type_get (gnutls_session_t session) +{ + return session->security_parameters.cert_type; +} + +/** + * gnutls_kx_get - Returns the key exchange algorithm. + * @session: is a #gnutls_session_t structure. + * + * Returns: the key exchange algorithm used in the last handshake. + **/ +gnutls_kx_algorithm_t +gnutls_kx_get (gnutls_session_t session) +{ + return session->security_parameters.kx_algorithm; +} + +/** + * gnutls_mac_get - Returns the currently used mac algorithm. + * @session: is a #gnutls_session_t structure. + * + * Returns: the currently used mac algorithm. + **/ +gnutls_mac_algorithm_t +gnutls_mac_get (gnutls_session_t session) +{ + return session->security_parameters.read_mac_algorithm; +} + +/** + * gnutls_compression_get - Returns the currently used compression algorithm. + * @session: is a #gnutls_session_t structure. + * + * Returns: the currently used compression method. + **/ +gnutls_compression_method_t +gnutls_compression_get (gnutls_session_t session) +{ + return session->security_parameters.read_compression_algorithm; +} + +/* Check if the given certificate type is supported. + * This means that it is enabled by the priority functions, + * and a matching certificate exists. + */ +int +_gnutls_session_cert_type_supported (gnutls_session_t session, + gnutls_certificate_type_t cert_type) +{ + unsigned i; + unsigned cert_found = 0; + gnutls_certificate_credentials_t cred; + + if (session->security_parameters.entity == GNUTLS_SERVER) + { + cred + = (gnutls_certificate_credentials_t) _gnutls_get_cred (session->key, + GNUTLS_CRD_CERTIFICATE, + NULL); + + if (cred == NULL) + return GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE; + + if (cred->server_get_cert_callback == NULL) + { + for (i = 0; i < cred->ncerts; i++) + { + if (cred->cert_list[i][0].cert_type == cert_type) + { + cert_found = 1; + break; + } + } + + if (cert_found == 0) + /* no certificate is of that type. + */ + return GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE; + } + } + + if (session->internals.priorities.cert_type.algorithms == 0 && cert_type + == DEFAULT_CERT_TYPE) + return 0; + + for (i = 0; i < session->internals.priorities.cert_type.algorithms; i++) + { + if (session->internals.priorities.cert_type.priority[i] == cert_type) + { + return 0; /* ok */ + } + } + + return GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE; +} + +/* this function deinitializes all the internal parameters stored + * in a session struct. + */ +inline static void +deinit_internal_params (gnutls_session_t session) +{ + if (session->internals.params.free_dh_params) + gnutls_dh_params_deinit (session->internals.params.dh_params); + + if (session->internals.params.free_rsa_params) + gnutls_rsa_params_deinit (session->internals.params.rsa_params); + + memset (&session->internals.params, 0, sizeof (session->internals.params)); +} + +/* This function will clear all the variables in internals + * structure within the session, which depend on the current handshake. + * This is used to allow further handshakes. + */ +void +_gnutls_handshake_internal_state_clear (gnutls_session_t session) +{ + session->internals.extensions_sent_size = 0; + + /* by default no selected certificate */ + session->internals.proposed_record_size = DEFAULT_MAX_RECORD_SIZE; + session->internals.adv_version_major = 0; + session->internals.adv_version_minor = 0; + session->internals.v2_hello = 0; + memset (&session->internals.handshake_header_buffer, 0, + sizeof (handshake_header_buffer_st)); + session->internals.adv_version_minor = 0; + session->internals.adv_version_minor = 0; + session->internals.direction = 0; + + /* use out of band data for the last + * handshake messages received. + */ + session->internals.last_handshake_in = -1; + session->internals.last_handshake_out = -1; + + session->internals.resumable = RESUME_TRUE; + _gnutls_free_datum (&session->internals.recv_buffer); + + deinit_internal_params (session); + +} + +#define MIN_DH_BITS 727 +/** + * gnutls_init - This function initializes the session to null (null encryption etc...). + * @con_end: indicate if this session is to be used for server or client. + * @session: is a pointer to a #gnutls_session_t structure. + * + * This function initializes the current session to null. Every + * session must be initialized before use, so internal structures can + * be allocated. This function allocates structures which can only + * be free'd by calling gnutls_deinit(). Returns zero on success. + * + * @con_end can be one of %GNUTLS_CLIENT and %GNUTLS_SERVER. + * + * Returns: %GNUTLS_E_SUCCESS on success, or an error code. + **/ + +// TODO rm redundent pointer ref +int +gnutls_init (gnutls_session_t * session, gnutls_connection_end_t con_end) +{ + *session = gnutls_calloc (1, sizeof (struct gnutls_session_int)); + if (*session == NULL) + return GNUTLS_E_MEMORY_ERROR; + + (*session)->security_parameters.entity = con_end; + + /* the default certificate type for TLS */ + (*session)->security_parameters.cert_type = DEFAULT_CERT_TYPE; + + /* Set the defaults for initial handshake */ + (*session)->security_parameters.read_bulk_cipher_algorithm = + (*session)->security_parameters.write_bulk_cipher_algorithm = + GNUTLS_CIPHER_NULL; + + (*session)->security_parameters.read_mac_algorithm = + (*session)->security_parameters.write_mac_algorithm = GNUTLS_MAC_NULL; + + (*session)->security_parameters.read_compression_algorithm + = GNUTLS_COMP_NULL; + (*session)->security_parameters.write_compression_algorithm + = GNUTLS_COMP_NULL; + + (*session)->internals.enable_private = 0; + + /* Initialize buffers */ + _gnutls_buffer_init (&(*session)->internals.application_data_buffer); + _gnutls_buffer_init (&(*session)->internals.handshake_data_buffer); + _gnutls_buffer_init (&(*session)->internals.handshake_hash_buffer); + _gnutls_buffer_init (&(*session)->internals.ia_data_buffer); + + _gnutls_buffer_init (&(*session)->internals.record_send_buffer); + _gnutls_buffer_init (&(*session)->internals.record_recv_buffer); + + _gnutls_buffer_init (&(*session)->internals.handshake_send_buffer); + _gnutls_buffer_init (&(*session)->internals.handshake_recv_buffer); + + (*session)->key = gnutls_calloc (1, sizeof (struct gnutls_key_st)); + if ((*session)->key == NULL) + { + cleanup_session:gnutls_free (*session); + *session = NULL; + return GNUTLS_E_MEMORY_ERROR; + } + + (*session)->internals.expire_time = DEFAULT_EXPIRE_TIME; /* one hour default */ + + gnutls_dh_set_prime_bits ((*session), MIN_DH_BITS); + + gnutls_transport_set_lowat ((*session), DEFAULT_LOWAT); /* the default for tcp */ + + gnutls_handshake_set_max_packet_length ((*session), + MAX_HANDSHAKE_PACKET_SIZE); + + /* Allocate a minimum size for recv_data + * This is allocated in order to avoid small messages, making + * the receive procedure slow. + */ + (*session)->internals.record_recv_buffer.data + = gnutls_malloc (INITIAL_RECV_BUFFER_SIZE); + if ((*session)->internals.record_recv_buffer.data == NULL) + { + gnutls_free ((*session)->key); + goto cleanup_session; + } + + /* set the socket pointers to -1; */ + (*session)->internals.transport_recv_ptr = (gnutls_transport_ptr_t) - 1; + (*session)->internals.transport_send_ptr = (gnutls_transport_ptr_t) - 1; + + /* set the default maximum record size for TLS + */ + (*session)->security_parameters.max_record_recv_size + = DEFAULT_MAX_RECORD_SIZE; + (*session)->security_parameters.max_record_send_size + = DEFAULT_MAX_RECORD_SIZE; + + /* everything else not initialized here is initialized + * as NULL or 0. This is why calloc is used. + */ + + _gnutls_handshake_internal_state_clear (*session); + + return 0; +} + +/* returns RESUME_FALSE or RESUME_TRUE. + */ +int +_gnutls_session_is_resumable (gnutls_session_t session) +{ + return session->internals.resumable; +} + +/** + * gnutls_deinit - This function clears all buffers associated with a session + * @session: is a #gnutls_session_t structure. + * + * This function clears all buffers associated with the @session. + * This function will also remove session data from the session + * database if the session was terminated abnormally. + **/ +void +gnutls_deinit (gnutls_session_t session) +{ + + if (session == NULL) + return; + + /* remove auth info firstly */ + _gnutls_free_auth_info (session); + + _gnutls_handshake_internal_state_clear (session); + _gnutls_handshake_io_buffer_clear (session); + + _gnutls_free_datum (&session->connection_state.read_mac_secret); + _gnutls_free_datum (&session->connection_state.write_mac_secret); + + _gnutls_buffer_clear (&session->internals.ia_data_buffer); + _gnutls_buffer_clear (&session->internals.handshake_hash_buffer); + _gnutls_buffer_clear (&session->internals.handshake_data_buffer); + _gnutls_buffer_clear (&session->internals.application_data_buffer); + _gnutls_buffer_clear (&session->internals.record_recv_buffer); + _gnutls_buffer_clear (&session->internals.record_send_buffer); + + gnutls_credentials_clear (session); + _gnutls_selected_certs_deinit (session); + + if (session->connection_state.read_cipher_state != NULL) + _gnutls_cipher_deinit (session->connection_state.read_cipher_state); + if (session->connection_state.write_cipher_state != NULL) + _gnutls_cipher_deinit (session->connection_state.write_cipher_state); + + if (session->connection_state.read_compression_state != NULL) + _gnutls_comp_deinit (session->connection_state.read_compression_state, 1); + if (session->connection_state.write_compression_state != NULL) + _gnutls_comp_deinit (session->connection_state. + write_compression_state, 0); + + _gnutls_free_datum (&session->cipher_specs.server_write_mac_secret); + _gnutls_free_datum (&session->cipher_specs.client_write_mac_secret); + _gnutls_free_datum (&session->cipher_specs.server_write_IV); + _gnutls_free_datum (&session->cipher_specs.client_write_IV); + _gnutls_free_datum (&session->cipher_specs.server_write_key); + _gnutls_free_datum (&session->cipher_specs.client_write_key); + + if (session->key != NULL) + { + _gnutls_mpi_release (&session->key->KEY); + _gnutls_mpi_release (&session->key->client_Y); + _gnutls_mpi_release (&session->key->client_p); + _gnutls_mpi_release (&session->key->client_g); + + _gnutls_mpi_release (&session->key->u); + _gnutls_mpi_release (&session->key->a); + _gnutls_mpi_release (&session->key->x); + _gnutls_mpi_release (&session->key->A); + _gnutls_mpi_release (&session->key->B); + _gnutls_mpi_release (&session->key->b); + + /* RSA */ + _gnutls_mpi_release (&session->key->rsa[0]); + _gnutls_mpi_release (&session->key->rsa[1]); + + _gnutls_mpi_release (&session->key->dh_secret); + gnutls_free (session->key); + + session->key = NULL; + } + + gnutls_free (session->internals.srp_username); + + if (session->internals.srp_password) + { + memset (session->internals.srp_password, 0, + strlen (session->internals.srp_password)); + gnutls_free (session->internals.srp_password); + } + + memset (session, 0, sizeof (struct gnutls_session_int)); + gnutls_free (session); +} + +/* Returns the minimum prime bits that are acceptable. + */ +int +_gnutls_dh_get_allowed_prime_bits (gnutls_session_t session) +{ + return session->internals.dh_prime_bits; +} + +int +_gnutls_dh_set_peer_public (gnutls_session_t session, mpi_t public) +{ + dh_info_st *dh; + int ret; + + switch (gnutls_auth_get_type (session)) + { + case GNUTLS_CRD_ANON: + { + anon_auth_info_t info; + info = _gnutls_get_auth_info (session); + if (info == NULL) + return GNUTLS_E_INTERNAL_ERROR; + + dh = &info->dh; + break; + } + case GNUTLS_CRD_CERTIFICATE: + { + cert_auth_info_t info; + + info = _gnutls_get_auth_info (session); + if (info == NULL) + return GNUTLS_E_INTERNAL_ERROR; + + dh = &info->dh; + break; + } + default: + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + ret = _gnutls_mpi_dprint_lz (&dh->public_key, public); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + return 0; +} + +int +_gnutls_dh_set_secret_bits (gnutls_session_t session, unsigned bits) +{ + switch (gnutls_auth_get_type (session)) + { + case GNUTLS_CRD_ANON: + { + anon_auth_info_t info; + info = _gnutls_get_auth_info (session); + if (info == NULL) + return GNUTLS_E_INTERNAL_ERROR; + info->dh.secret_bits = bits; + break; + } + case GNUTLS_CRD_CERTIFICATE: + { + cert_auth_info_t info; + + info = _gnutls_get_auth_info (session); + if (info == NULL) + return GNUTLS_E_INTERNAL_ERROR; + + info->dh.secret_bits = bits; + break; + default: + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + } + + return 0; +} + +/* This function will set in the auth info structure the + * RSA exponent and the modulus. + */ +int +_gnutls_rsa_export_set_pubkey (gnutls_session_t session, + mpi_t exponent, mpi_t modulus) +{ + cert_auth_info_t info; + int ret; + + info = _gnutls_get_auth_info (session); + if (info == NULL) + return GNUTLS_E_INTERNAL_ERROR; + + ret = _gnutls_mpi_dprint_lz (&info->rsa_export.modulus, modulus); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + ret = _gnutls_mpi_dprint_lz (&info->rsa_export.exponent, exponent); + if (ret < 0) + { + gnutls_assert (); + _gnutls_free_datum (&info->rsa_export.modulus); + return ret; + } + + return 0; +} + +/* Sets the prime and the generator in the auth info structure. + */ +int +_gnutls_dh_set_group (gnutls_session_t session, mpi_t gen, mpi_t prime) +{ + dh_info_st *dh; + int ret; + + switch (gnutls_auth_get_type (session)) + { + case GNUTLS_CRD_ANON: + { + anon_auth_info_t info; + info = _gnutls_get_auth_info (session); + if (info == NULL) + return GNUTLS_E_INTERNAL_ERROR; + + dh = &info->dh; + break; + } + case GNUTLS_CRD_CERTIFICATE: + { + cert_auth_info_t info; + + info = _gnutls_get_auth_info (session); + if (info == NULL) + return GNUTLS_E_INTERNAL_ERROR; + + dh = &info->dh; + break; + } + default: + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + /* prime + */ + ret = _gnutls_mpi_dprint_lz (&dh->prime, prime); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + /* generator + */ + ret = _gnutls_mpi_dprint_lz (&dh->generator, gen); + if (ret < 0) + { + gnutls_assert (); + _gnutls_free_datum (&dh->prime); + return ret; + } + + return 0; +} + +/** + * gnutls_openpgp_send_cert - This function will order gnutls to send the openpgp fingerprint instead of the key + * @session: is a pointer to a #gnutls_session_t structure. + * @status: is one of GNUTLS_OPENPGP_CERT, or GNUTLS_OPENPGP_CERT_FINGERPRINT + * + * This function will order gnutls to send the key fingerprint + * instead of the key in the initial handshake procedure. This should + * be used with care and only when there is indication or knowledge + * that the server can obtain the client's key. + **/ +void +gnutls_openpgp_send_cert (gnutls_session_t session, + gnutls_openpgp_crt_status_t status) +{ + session->internals.pgp_fingerprint = status; +} + +/** + * gnutls_certificate_send_x509_rdn_sequence - This function will order gnutls to send or not the x.509 rdn sequence + * @session: is a pointer to a #gnutls_session_t structure. + * @status: is 0 or 1 + * + * If status is non zero, this function will order gnutls not to send + * the rdnSequence in the certificate request message. That is the + * server will not advertize it's trusted CAs to the peer. If status + * is zero then the default behaviour will take effect, which is to + * advertize the server's trusted CAs. + * + * This function has no effect in clients, and in authentication + * methods other than certificate with X.509 certificates. + **/ +void +gnutls_certificate_send_x509_rdn_sequence (gnutls_session_t session, + int status) +{ + session->internals.ignore_rdn_sequence = status; +} + +int +_gnutls_openpgp_send_fingerprint (gnutls_session_t session) +{ + return session->internals.pgp_fingerprint; +} + +/*- + * _gnutls_record_set_default_version - Used to set the default version for the first record packet + * @session: is a #gnutls_session_t structure. + * @major: is a tls major version + * @minor: is a tls minor version + * + * This function sets the default version that we will use in the first + * record packet (client hello). This function is only useful to people + * that know TLS internals and want to debug other implementations. + * + -*/ +void +_gnutls_record_set_default_version (gnutls_session_t session, + unsigned char major, unsigned char minor) +{ + session->internals.default_record_version[0] = major; + session->internals.default_record_version[1] = minor; +} + +/** + * gnutls_handshake_set_private_extensions - Used to enable the private cipher suites + * @session: is a #gnutls_session_t structure. + * @allow: is an integer (0 or 1) + * + * This function will enable or disable the use of private cipher + * suites (the ones that start with 0xFF). By default or if @allow + * is 0 then these cipher suites will not be advertized nor used. + * + * Unless this function is called with the option to allow (1), then + * no compression algorithms, like LZO. That is because these + * algorithms are not yet defined in any RFC or even internet draft. + * + * Enabling the private ciphersuites when talking to other than + * gnutls servers and clients may cause interoperability problems. + **/ +void +gnutls_handshake_set_private_extensions (gnutls_session_t session, int allow) +{ + session->internals.enable_private = allow; +} + +inline static int +_gnutls_cal_PRF_A (gnutls_mac_algorithm_t algorithm, + const void *secret, + int secret_size, + const void *seed, int seed_size, void *result) +{ + mac_hd_t td1; + + td1 = _gnutls_hmac_init (algorithm, secret, secret_size); + if (td1 == GNUTLS_MAC_FAILED) + { + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + _gnutls_hmac (td1, seed, seed_size); + _gnutls_hmac_deinit (td1, result); + + return 0; +} + +#define MAX_SEED_SIZE 200 + +/* Produces "total_bytes" bytes using the hash algorithm specified. + * (used in the PRF function) + */ +static int +_gnutls_P_hash (gnutls_mac_algorithm_t algorithm, + const opaque * secret, + int secret_size, + const opaque * seed, + int seed_size, int total_bytes, opaque * ret) +{ + + mac_hd_t td2; + int i, times, how, blocksize, A_size; + opaque final[20], Atmp[MAX_SEED_SIZE]; + int output_bytes, result; + + if (seed_size > MAX_SEED_SIZE || total_bytes <= 0) + { + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + blocksize = _gnutls_hmac_get_algo_len (algorithm); + + output_bytes = 0; + do + { + output_bytes += blocksize; + } + while (output_bytes < total_bytes); + + /* calculate A(0) */ + + memcpy (Atmp, seed, seed_size); + A_size = seed_size; + + times = output_bytes / blocksize; + + for (i = 0; i < times; i++) + { + td2 = _gnutls_hmac_init (algorithm, secret, secret_size); + if (td2 == GNUTLS_MAC_FAILED) + { + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + /* here we calculate A(i+1) */ + if ((result = _gnutls_cal_PRF_A (algorithm, secret, secret_size, Atmp, + A_size, Atmp)) < 0) + { + gnutls_assert (); + _gnutls_hmac_deinit (td2, final); + return result; + } + + A_size = blocksize; + + _gnutls_hmac (td2, Atmp, A_size); + _gnutls_hmac (td2, seed, seed_size); + _gnutls_hmac_deinit (td2, final); + + if ((1 + i) * blocksize < total_bytes) + { + how = blocksize; + } + else + { + how = total_bytes - (i) * blocksize; + } + + if (how > 0) + { + memcpy (&ret[i * blocksize], final, how); + } + } + + return 0; +} + +/* Xor's two buffers and puts the output in the first one. + */ +inline static void +_gnutls_xor (opaque * o1, opaque * o2, int length) +{ + int i; + for (i = 0; i < length; i++) + { + o1[i] ^= o2[i]; + } +} + +#define MAX_PRF_BYTES 200 + +/* The PRF function expands a given secret + * needed by the TLS specification. ret must have a least total_bytes + * available. + */ +int +_gnutls_PRF (gnutls_session_t session, + const opaque * secret, + int secret_size, + const char *label, + int label_size, + const opaque * seed, int seed_size, int total_bytes, void *ret) +{ + int l_s, s_seed_size; + const opaque *s1, *s2; + opaque s_seed[MAX_SEED_SIZE]; + opaque o1[MAX_PRF_BYTES], o2[MAX_PRF_BYTES]; + int result; + gnutls_protocol_t ver = gnutls_protocol_get_version (session); + + if (total_bytes > MAX_PRF_BYTES) + { + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + /* label+seed = s_seed */ + s_seed_size = seed_size + label_size; + + if (s_seed_size > MAX_SEED_SIZE) + { + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + memcpy (s_seed, label, label_size); + memcpy (&s_seed[label_size], seed, seed_size); + + if (ver >= GNUTLS_TLS1_2) + { + result = _gnutls_P_hash (GNUTLS_MAC_SHA1, secret, secret_size, s_seed, + s_seed_size, total_bytes, ret); + if (result < 0) + { + gnutls_assert (); + return result; + } + } + else + { + l_s = secret_size / 2; + + s1 = &secret[0]; + s2 = &secret[l_s]; + + if (secret_size % 2 != 0) + { + l_s++; + } + + result = _gnutls_P_hash (GNUTLS_MAC_MD5, s1, l_s, s_seed, s_seed_size, + total_bytes, o1); + if (result < 0) + { + gnutls_assert (); + return result; + } + + result = _gnutls_P_hash (GNUTLS_MAC_SHA1, s2, l_s, s_seed, s_seed_size, + total_bytes, o2); + if (result < 0) + { + gnutls_assert (); + return result; + } + + _gnutls_xor (o1, o2, total_bytes); + + memcpy (ret, o1, total_bytes); + } + + return 0; /* ok */ + +} + +/** + * gnutls_prf_raw - access the TLS PRF directly + * @session: is a #gnutls_session_t structure. + * @label_size: length of the @label variable. + * @label: label used in PRF computation, typically a short string. + * @seed_size: length of the @seed variable. + * @seed: optional extra data to seed the PRF with. + * @outsize: size of pre-allocated output buffer to hold the output. + * @out: pre-allocate buffer to hold the generated data. + * + * Apply the TLS Pseudo-Random-Function (PRF) using the master secret + * on some data. + * + * The @label variable usually contain a string denoting the purpose + * for the generated data. The @seed usually contain data such as the + * client and server random, perhaps together with some additional + * data that is added to guarantee uniqueness of the output for a + * particular purpose. + * + * Because the output is not guaranteed to be unique for a particular + * session unless @seed include the client random and server random + * fields (the PRF would output the same data on another connection + * resumed from the first one), it is not recommended to use this + * function directly. The gnutls_prf() function seed the PRF with the + * client and server random fields directly, and is recommended if you + * want to generate pseudo random data unique for each session. + * + * Returns: %GNUTLS_E_SUCCESS on success, or an error code. + **/ +int +gnutls_prf_raw (gnutls_session_t session, + size_t label_size, + const char *label, + size_t seed_size, const char *seed, size_t outsize, char *out) +{ + int ret; + + ret = _gnutls_PRF (session, session->security_parameters.master_secret, + TLS_MASTER_SIZE, label, label_size, (opaque *) seed, + seed_size, outsize, out); + + return ret; +} + +/** + * gnutls_prf - derive pseudo-random data using the TLS PRF + * @session: is a #gnutls_session_t structure. + * @label_size: length of the @label variable. + * @label: label used in PRF computation, typically a short string. + * @server_random_first: non-0 if server random field should be first in seed + * @extra_size: length of the @extra variable. + * @extra: optional extra data to seed the PRF with. + * @outsize: size of pre-allocated output buffer to hold the output. + * @out: pre-allocate buffer to hold the generated data. + * + * Apply the TLS Pseudo-Random-Function (PRF) using the master secret + * on some data, seeded with the client and server random fields. + * + * The @label variable usually contain a string denoting the purpose + * for the generated data. The @server_random_first indicate whether + * the client random field or the server random field should be first + * in the seed. Non-0 indicate that the server random field is first, + * 0 that the client random field is first. + * + * The @extra variable can be used to add more data to the seed, after + * the random variables. It can be used to tie make sure the + * generated output is strongly connected to some additional data + * (e.g., a string used in user authentication). + * + * The output is placed in *@OUT, which must be pre-allocated. + * + * Returns: %GNUTLS_E_SUCCESS on success, or an error code. + **/ +int +gnutls_prf (gnutls_session_t session, + size_t label_size, + const char *label, + int server_random_first, + size_t extra_size, const char *extra, size_t outsize, char *out) +{ + int ret; + opaque *seed; + size_t seedsize = 2 * TLS_RANDOM_SIZE + extra_size; + + seed = gnutls_malloc (seedsize); + if (!seed) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + memcpy (seed, + server_random_first ? session->security_parameters.server_random + : session->security_parameters.client_random, TLS_RANDOM_SIZE); + memcpy (seed + TLS_RANDOM_SIZE, + server_random_first ? session->security_parameters.client_random + : session->security_parameters.server_random, TLS_RANDOM_SIZE); + + memcpy (seed + 2 * TLS_RANDOM_SIZE, extra, extra_size); + + ret = _gnutls_PRF (session, session->security_parameters.master_secret, + TLS_MASTER_SIZE, label, label_size, seed, seedsize, + outsize, out); + + gnutls_free (seed); + + return ret; +} + +/** + * gnutls_session_get_client_random - get the session's client random value + * @session: is a #gnutls_session_t structure. + * + * Return a pointer to the 32-byte client random field used in the + * session. The pointer must not be modified or deallocated. + * + * If a client random value has not yet been established, the output + * will be garbage; in particular, a %NULL return value should not be + * expected. + * + * Returns: pointer to client random data. + **/ +const void * +gnutls_session_get_client_random (gnutls_session_t session) +{ + return (char *) session->security_parameters.client_random; +} + +/** + * gnutls_session_get_server_random - get the session's server random value + * @session: is a #gnutls_session_t structure. + * + * Return a pointer to the 32-byte server random field used in the + * session. The pointer must not be modified or deallocated. + * + * If a server random value has not yet been established, the output + * will be garbage; in particular, a %NULL return value should not be + * expected. + * + * Returns: pointer to server random data. + **/ +const void * +gnutls_session_get_server_random (gnutls_session_t session) +{ + return (char *) session->security_parameters.server_random; +} + +/** + * gnutls_session_get_master_secret - get the session's master secret value + * @session: is a #gnutls_session_t structure. + * + * Return a pointer to the 48-byte master secret in the session. The + * pointer must not be modified or deallocated. + * + * If a master secret value has not yet been established, the output + * will be garbage; in particular, a %NULL return value should not be + * expected. + * + * Consider using gnutls_prf() rather than extracting the master + * secret and use it to derive further data. + * + * Returns: pointer to master secret data. + **/ +const void * +gnutls_session_get_master_secret (gnutls_session_t session) +{ + return (char *) session->security_parameters.master_secret; +} + +/** + * gnutls_session_is_resumed - Used to check whether this session is a resumed one + * @session: is a #gnutls_session_t structure. + * + * Returns: non zero if this session is resumed, or a zero if this is + * a new session. + **/ +int +gnutls_session_is_resumed (gnutls_session_t session) +{ + if (session->security_parameters.entity == GNUTLS_CLIENT) + { + if (session->security_parameters.session_id_size > 0 + && session->security_parameters.session_id_size + == session->internals.resumed_security_parameters.session_id_size + && memcmp (session->security_parameters.session_id, + session->internals.resumed_security_parameters. + session_id, session->security_parameters.session_id_size) + == 0) + return 1; + } + else + { + if (session->internals.resumed == RESUME_TRUE) + return 1; + } + + return 0; +} + +/*- + * _gnutls_session_is_export - Used to check whether this session is of export grade + * @session: is a #gnutls_session_t structure. + * + * This function will return non zero if this session is of export grade. + * + -*/ +int +_gnutls_session_is_export (gnutls_session_t session) +{ + gnutls_cipher_algorithm_t cipher; + + cipher = + _gnutls_cipher_suite_get_cipher_algo (&session->security_parameters. + current_cipher_suite); + + if (_gnutls_cipher_get_export_flag (cipher) != 0) + return 1; + + return 0; +} + +/** + * gnutls_session_get_ptr - Used to get the user pointer from the session structure + * @session: is a #gnutls_session_t structure. + * + * Returns: the user given pointer from the session structure. This + * is the pointer set with gnutls_session_set_ptr(). + **/ +void * +gnutls_session_get_ptr (gnutls_session_t session) +{ + return session->internals.user_ptr; +} + +/** + * gnutls_session_set_ptr - Used to set the user pointer to the session structure + * @session: is a #gnutls_session_t structure. + * @ptr: is the user pointer + * + * This function will set (associate) the user given pointer to the + * session structure. This is pointer can be accessed with + * gnutls_session_get_ptr(). + **/ +void +gnutls_session_set_ptr (gnutls_session_t session, void *ptr) +{ + session->internals.user_ptr = ptr; +} + +/** + * gnutls_record_get_direction - This function will return the direction of the last interrupted function call + * @session: is a #gnutls_session_t structure. + * + * This function provides information about the internals of the + * record protocol and is only useful if a prior gnutls function call + * (e.g. gnutls_handshake()) was interrupted for some reason, that + * is, if a function returned %GNUTLS_E_INTERRUPTED or + * %GNUTLS_E_AGAIN. In such a case, you might want to call select() + * or poll() before calling the interrupted gnutls function again. + * To tell you whether a file descriptor should be selected for + * either reading or writing, gnutls_record_get_direction() returns 0 + * if the interrupted function was trying to read data, and 1 if it + * was trying to write data. + * + * Returns: 0 if trying to read data, 1 if trying to write data. + **/ +int +gnutls_record_get_direction (gnutls_session_t session) +{ + return session->internals.direction; +} + +/*- + * _gnutls_rsa_pms_set_version - Sets a version to be used at the RSA PMS + * @session: is a #gnutls_session_t structure. + * @major: is the major version to use + * @minor: is the minor version to use + * + * This function will set the given version number to be used at the + * RSA PMS secret. This is only useful to clients, which want to + * test server's capabilities. + * + -*/ +void +_gnutls_rsa_pms_set_version (gnutls_session_t session, + unsigned char major, unsigned char minor) +{ + session->internals.rsa_pms_version[0] = major; + session->internals.rsa_pms_version[1] = minor; +} + +/** + * gnutls_handshake_set_post_client_hello_function - This function will a callback to be called after the client hello is received + * @res: is a gnutls_anon_server_credentials_t structure + * @func: is the function to be called + * + * This function will set a callback to be called after the client + * hello has been received (callback valid in server side only). This + * allows the server to adjust settings based on received extensions. + * + * Those settings could be ciphersuites, requesting certificate, or + * anything else except for version negotiation (this is done before + * the hello message is parsed). + * + * This callback must return 0 on success or a gnutls error code to + * terminate the handshake. + * + * NOTE: You should not use this function to terminate the handshake + * based on client input unless you know what you are doing. Before + * the handshake is finished there is no way to know if there is a + * man-in-the-middle attack being performed. + * + **/ +void +gnutls_handshake_set_post_client_hello_function (gnutls_session_t session, + gnutls_handshake_post_client_hello_func + func) +{ + session->internals.user_hello_func = func; +} + +/** + * gnutls_session_enable_compatibility_mode - Used to disable certain features in TLS in order to honour compatibility + * @session: is a #gnutls_session_t structure. + * + * This function can be used to disable certain (security) features + * in TLS in order to maintain maximum compatibility with buggy + * clients. It is equivalent to calling: + * gnutls_record_disable_padding() + * + * Normally only servers that require maximum compatibility with + * everything out there, need to call this function. + **/ +void +gnutls_session_enable_compatibility_mode (gnutls_session_t session) +{ + gnutls_record_disable_padding (session); +} diff --git a/src/daemon/https/tls/gnutls_state.h b/src/daemon/https/tls/gnutls_state.h new file mode 100644 index 00000000..7a920ee4 --- /dev/null +++ b/src/daemon/https/tls/gnutls_state.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +#ifndef GNUTLS_STATE_H +# define GNUTLS_STATE_H + +#include + +void _gnutls_session_cert_type_set (gnutls_session_t session, + gnutls_certificate_type_t); +gnutls_kx_algorithm_t gnutls_kx_get (gnutls_session_t session); +gnutls_cipher_algorithm_t gnutls_cipher_get (gnutls_session_t session); +gnutls_certificate_type_t gnutls_certificate_type_get (gnutls_session_t); + +#include + +#define CHECK_AUTH(auth, ret) if (gnutls_auth_get_type(session) != auth) { \ + gnutls_assert(); \ + return ret; \ + } + +#endif + +int _gnutls_session_cert_type_supported (gnutls_session_t, + gnutls_certificate_type_t); + +int _gnutls_dh_set_secret_bits (gnutls_session_t session, unsigned bits); + +int _gnutls_dh_set_peer_public (gnutls_session_t session, mpi_t public); +int _gnutls_dh_set_group (gnutls_session_t session, mpi_t gen, mpi_t prime); + +int _gnutls_dh_get_allowed_prime_bits (gnutls_session_t session); +void _gnutls_handshake_internal_state_clear (gnutls_session_t); + +int _gnutls_rsa_export_set_pubkey (gnutls_session_t session, + mpi_t exponent, mpi_t modulus); + +int _gnutls_session_is_resumable (gnutls_session_t session); +int _gnutls_session_is_export (gnutls_session_t session); + +int _gnutls_openpgp_send_fingerprint (gnutls_session_t session); + +int _gnutls_PRF (gnutls_session_t session, + const opaque * secret, int secret_size, + const char *label, int label_size, + const opaque * seed, int seed_size, + int total_bytes, void *ret); + +int gnutls_init (gnutls_session_t * session, gnutls_connection_end_t con_end); + +#define DEFAULT_CERT_TYPE GNUTLS_CRT_X509 diff --git a/src/daemon/https/tls/gnutls_str.c b/src/daemon/https/tls/gnutls_str.c new file mode 100644 index 00000000..0ccf8445 --- /dev/null +++ b/src/daemon/https/tls/gnutls_str.c @@ -0,0 +1,312 @@ +/* + * Copyright (C) 2002, 2004, 2005, 2007 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +#include +#include +#include +#include + +/* These function are like strcat, strcpy. They only + * do bound checking (they shouldn't cause buffer overruns), + * and they always produce null terminated strings. + * + * They should be used only with null terminated strings. + */ +void _gnutls_str_cat(char *dest, + size_t dest_tot_size, + const char *src) +{ + size_t str_size = strlen(src); + size_t dest_size = strlen(dest); + + if (dest_tot_size - dest_size > str_size) + { + strcat(dest, src); + } + else + { + if (dest_tot_size - dest_size > 0) + { + strncat(dest, src, (dest_tot_size - dest_size) - 1); + dest[dest_tot_size - 1] = 0; + } + } +} + +void _gnutls_str_cpy(char *dest, + size_t dest_tot_size, + const char *src) +{ + size_t str_size = strlen(src); + + if (dest_tot_size > str_size) + { + strcpy(dest, src); + } + else + { + if (dest_tot_size > 0) + { + strncpy(dest, src, (dest_tot_size) - 1); + dest[dest_tot_size - 1] = 0; + } + } +} + +void _gnutls_mem_cpy(char *dest, + size_t dest_tot_size, + const char *src, + size_t src_size) +{ + + if (dest_tot_size >= src_size) + { + memcpy(dest, src, src_size); + } + else + { + if (dest_tot_size > 0) + { + memcpy(dest, src, dest_tot_size); + } + } +} + +void _gnutls_string_init(gnutls_string * str, + gnutls_alloc_function alloc_func, + gnutls_realloc_function realloc_func, + gnutls_free_function free_func) +{ + str->data = NULL; + str->max_length = 0; + str->length = 0; + + str->alloc_func = alloc_func; + str->free_func = free_func; + str->realloc_func = realloc_func; +} + +void _gnutls_string_clear(gnutls_string * str) +{ + if (str == NULL || str->data == NULL) + return; + str->free_func(str->data); + + str->data = NULL; + str->max_length = 0; + str->length = 0; +} + +/* This one does not copy the string. + */ +gnutls_datum_t _gnutls_string2datum(gnutls_string * str) +{ + gnutls_datum_t ret; + + ret.data = str->data; + ret.size = str->length; + + return ret; +} + +#define MIN_CHUNK 256 + +int _gnutls_string_copy_str(gnutls_string * dest, + const char *src) +{ + size_t src_len = strlen(src); + size_t max; + if (dest->max_length >= src_len) + { + memcpy(dest->data, src, src_len); + dest->length = src_len; + + return src_len; + } + else + { + max = (src_len > MIN_CHUNK) ? src_len : MIN_CHUNK; + dest->data = dest->realloc_func(dest->data, max); + if (dest->data == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + dest->max_length = MAX (MIN_CHUNK, src_len); + + memcpy(dest->data, src, src_len); + dest->length = src_len; + + return src_len; + } +} + +int _gnutls_string_append_str(gnutls_string * dest, + const char *src) +{ + size_t src_len = strlen(src); + size_t tot_len = src_len + dest->length; + + if (dest->max_length >= tot_len) + { + memcpy(&dest->data[dest->length], src, src_len); + dest->length = tot_len; + + return tot_len; + } + else + { + size_t new_len= + MAX (src_len, MIN_CHUNK) + MAX (dest->max_length, MIN_CHUNK); + + dest->data = dest->realloc_func(dest->data, new_len); + if (dest->data == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + dest->max_length = new_len; + + memcpy(&dest->data[dest->length], src, src_len); + dest->length = tot_len; + + return tot_len; + } +} + +int _gnutls_string_append_data(gnutls_string * dest, + const void *data, + size_t data_size) +{ + size_t tot_len = data_size + dest->length; + + if (dest->max_length >= tot_len) + { + memcpy(&dest->data[dest->length], data, data_size); + dest->length = tot_len; + + return tot_len; + } + else + { + size_t new_len= + MAX (data_size, MIN_CHUNK) + MAX (dest->max_length, MIN_CHUNK); + + dest->data = dest->realloc_func(dest->data, new_len); + if (dest->data == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + dest->max_length = new_len; + + memcpy(&dest->data[dest->length], data, data_size); + dest->length = tot_len; + + return tot_len; + } +} + +int _gnutls_string_append_printf(gnutls_string * dest, + const char *fmt, + ...) +{ + va_list args; + int len; + char *str; + + va_start(args, fmt); + len = vasprintf(&str, fmt, args); + va_end(args); + + if (len < 0 || !str) + return -1; + + len = _gnutls_string_append_str(dest, str); + + free(str); + + return len; +} + +/* Converts the given string (old) to hex. A buffer must be provided + * to hold the new hex string. The new string will be null terminated. + * If the buffer does not have enough space to hold the string, a + * truncated hex string is returned (always null terminated). + */ +char * _gnutls_bin2hex(const void *_old, + size_t oldlen, + char *buffer, + size_t buffer_size) +{ + unsigned int i, j; + const opaque *old = _old; + + for (i = j = 0; i < oldlen && j + 2 < buffer_size; j += 2) + { + sprintf(&buffer[j], "%.2x", old[i]); + i++; + } + buffer[j] = '\0'; + + return buffer; +} + +/* just a hex2bin function. + */ +int _gnutls_hex2bin(const opaque * hex_data, + int hex_size, + opaque * bin_data, + size_t * bin_size) +{ + int i, j; + opaque hex2_data[3]; + unsigned long val; + + /* FIXME: we don't handle whitespace. + */ + hex_size /= 2; + + if (*bin_size < (size_t) hex_size) + { + gnutls_assert (); + return GNUTLS_E_SHORT_MEMORY_BUFFER; + } + + for (i = j = 0; j < hex_size; i += 2, j++) + { + hex2_data[0] = hex_data[i]; + hex2_data[1] = hex_data[i + 1]; + hex2_data[2] = 0; + val = strtoul((char *) hex2_data, NULL, 16); + if (val == ULONG_MAX) + { + gnutls_assert (); + return GNUTLS_E_SRP_PWD_PARSING_ERROR; + } + bin_data[j] = val; + } + + return 0; +} diff --git a/src/daemon/https/tls/gnutls_str.h b/src/daemon/https/tls/gnutls_str.h new file mode 100644 index 00000000..c805d70f --- /dev/null +++ b/src/daemon/https/tls/gnutls_str.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2007 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +#ifndef GNUTLS_STR_H +# define GNUTLS_STR_H + +#include + +#define MAX(X,Y) ((X) > (Y) ? (X) : (Y)); + +void _gnutls_str_cpy (char *dest, size_t dest_tot_size, const char *src); +void _gnutls_mem_cpy (char *dest, size_t dest_tot_size, const char *src, + size_t src_size); +void _gnutls_str_cat (char *dest, size_t dest_tot_size, const char *src); + +typedef struct +{ + opaque * data; + size_t max_length; + size_t length; + gnutls_realloc_function realloc_func; + gnutls_alloc_function alloc_func; + gnutls_free_function free_func; +} gnutls_string; + +void _gnutls_string_init (gnutls_string *, gnutls_alloc_function, + gnutls_realloc_function, gnutls_free_function); +void _gnutls_string_clear (gnutls_string *); + +/* Beware, do not clear the string, after calling this + * function + */ +gnutls_datum_t _gnutls_string2datum (gnutls_string * str); + +int _gnutls_string_copy_str (gnutls_string * dest, const char *src); +int _gnutls_string_append_str (gnutls_string *, const char *str); +int _gnutls_string_append_data (gnutls_string *, const void *data, + size_t data_size); +int _gnutls_string_append_printf (gnutls_string * dest, const char *fmt, ...); + +char *_gnutls_bin2hex (const void *old, size_t oldlen, char *buffer, + size_t buffer_size); +int _gnutls_hex2bin (const opaque * hex_data, int hex_size, opaque * bin_data, + size_t * bin_size); + +#endif diff --git a/src/daemon/https/tls/gnutls_supplemental.c b/src/daemon/https/tls/gnutls_supplemental.c new file mode 100644 index 00000000..a47b9aaa --- /dev/null +++ b/src/daemon/https/tls/gnutls_supplemental.c @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2007 Free Software Foundation + * + * Author: Simon Josefsson + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +/* This file contains support functions for 'TLS Handshake Message for + * Supplemental Data' (RFC 4680). + * + * The idea here is simple. gnutls_handshake() in gnuts_handshake.c + * will call _gnutls_gen_supplemental and _gnutls_parse_supplemental + * when some extension requested that supplemental data be sent or + * received. Extension request this by setting the flags + * do_recv_supplemental or do_send_supplemental in the session. + * + * The functions in this file iterate through the _gnutls_supplemental + * array, and calls the send/recv functions for each respective data + * type. + * + * The receive function of each data type is responsible for decoding + * its own data. If the extension did not expect to receive + * supplemental data, it should return GNUTLS_E_UNEXPECTED_PACKET. + * Otherwise, it just parse the data as normal. + * + * The send function needs to append the 2-byte data format type, and + * append the 2-byte length of its data, and the data. If it doesn't + * want to send any data, it is fine to return without doing anything. + */ + +#include "gnutls_int.h" +#include "gnutls_supplemental.h" +#include "gnutls_errors.h" +#include "gnutls_num.h" + +typedef int (*supp_recv_func) (gnutls_session_t session, + const opaque * data, size_t data_size); +typedef int (*supp_send_func) (gnutls_session_t session, gnutls_buffer * buf); + +typedef struct +{ + const char *name; + gnutls_supplemental_data_format_type_t type; + supp_recv_func supp_recv_func; + supp_send_func supp_send_func; +} gnutls_supplemental_entry; + +gnutls_supplemental_entry _gnutls_supplemental[] = { + {0, 0, 0, 0} +}; + +const char * +gnutls_supplemental_get_name (gnutls_supplemental_data_format_type_t type) +{ + gnutls_supplemental_entry *p; + + for (p = _gnutls_supplemental; p->name != NULL; p++) + if (p->type == type) + return p->name; + + return NULL; +} + +static supp_recv_func +get_supp_func_recv (gnutls_supplemental_data_format_type_t type) +{ + gnutls_supplemental_entry *p; + + for (p = _gnutls_supplemental; p->name != NULL; p++) + if (p->type == type) + return p->supp_recv_func; + + return NULL; +} + +int +_gnutls_gen_supplemental (gnutls_session_t session, gnutls_buffer * buf) +{ + gnutls_supplemental_entry *p; + int ret; + + /* Make room for 3 byte length field. */ + ret = _gnutls_buffer_append (buf, "\0\0\0", 3); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + for (p = _gnutls_supplemental; p->name; p++) + { + supp_send_func supp_send = p->supp_send_func; + size_t sizepos = buf->length; + int ret; + + /* Make room for supplement type and length byte length field. */ + ret = _gnutls_buffer_append (buf, "\0\0\0\0", 4); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + ret = supp_send (session, buf); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + /* If data were added, store type+length, otherwise reset. */ + if (buf->length > sizepos + 4) + { + buf->data[sizepos] = 0; + buf->data[sizepos + 1] = p->type; + buf->data[sizepos + 2] = ((buf->length - sizepos - 4) >> 8) & 0xFF; + buf->data[sizepos + 3] = (buf->length - sizepos - 4) & 0xFF; + } + else + buf->length -= 4; + } + + buf->data[0] = ((buf->length - 3) >> 16) & 0xFF; + buf->data[1] = ((buf->length - 3) >> 8) & 0xFF; + buf->data[2] = (buf->length - 3) & 0xFF; + + _gnutls_debug_log ("EXT[%x]: Sending %d bytes of supplemental data\n", + session, buf->length); + + return buf->length; +} + +int +_gnutls_parse_supplemental (gnutls_session_t session, + const uint8_t * data, int datalen) +{ + const opaque *p = data; + ssize_t dsize = datalen; + size_t total_size; + + DECR_LEN (dsize, 3); + total_size = _gnutls_read_uint24 (p); + p += 3; + + if (dsize != total_size) + { + gnutls_assert (); + return GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER; + } + + do + { + uint16_t supp_data_type; + uint16_t supp_data_length; + supp_recv_func recv_func; + + DECR_LEN (dsize, 2); + supp_data_type = _gnutls_read_uint16 (p); + p += 2; + + DECR_LEN (dsize, 2); + supp_data_length = _gnutls_read_uint16 (p); + p += 2; + + _gnutls_debug_log ("EXT[%x]: Got supplemental type=%02x length=%d\n", + session, supp_data_type, supp_data_length); + + recv_func = get_supp_func_recv (supp_data_type); + if (recv_func) + { + int ret = recv_func (session, p, supp_data_length); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + } + else + { + gnutls_assert (); + return GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER; + } + + DECR_LEN (dsize, supp_data_length); + p += supp_data_length; + } + while (dsize > 0); + + return 0; +} diff --git a/src/daemon/https/tls/gnutls_supplemental.h b/src/daemon/https/tls/gnutls_supplemental.h new file mode 100644 index 00000000..0b9c1207 --- /dev/null +++ b/src/daemon/https/tls/gnutls_supplemental.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2007 Free Software Foundation + * + * Author: Simon Josefsson + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +#include + +int _gnutls_parse_supplemental (gnutls_session_t session, + const uint8_t *data, + int data_size); +int _gnutls_gen_supplemental (gnutls_session_t session, + gnutls_buffer *buf); diff --git a/src/daemon/https/tls/gnutls_ui.c b/src/daemon/https/tls/gnutls_ui.c new file mode 100644 index 00000000..49ed2e96 --- /dev/null +++ b/src/daemon/https/tls/gnutls_ui.c @@ -0,0 +1,627 @@ +/* + * Copyright (C) 2001, 2002, 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +/* This file contains certificate authentication functions to be exported in the + * API and did not fit elsewhere. + */ + +#include +#include +#include +#include +#include +#include +#include + +/* ANON & DHE */ + +/** + * gnutls_dh_set_prime_bits - Used to set the bits for a DH ciphersuite + * @session: is a #gnutls_session_t structure. + * @bits: is the number of bits + * + * This function sets the number of bits, for use in an + * Diffie Hellman key exchange. This is used both in DH ephemeral and + * DH anonymous cipher suites. This will set the + * minimum size of the prime that will be used for the handshake. + * + * In the client side it sets the minimum accepted number of bits. + * If a server sends a prime with less bits than that + * GNUTLS_E_DH_PRIME_UNACCEPTABLE will be returned by the + * handshake. + * + **/ +void +gnutls_dh_set_prime_bits (gnutls_session_t session, unsigned int bits) +{ + session->internals.dh_prime_bits = bits; +} + +/** + * gnutls_dh_get_group - This function returns the group of the DH authentication + * @session: is a gnutls session + * @raw_gen: will hold the generator. + * @raw_prime: will hold the prime. + * + * This function will return the group parameters used in the last Diffie Hellman + * authentication with the peer. These are the prime and the generator used. + * This function should be used for both anonymous and ephemeral diffie Hellman. + * The output parameters must be freed with gnutls_free(). + * + * Returns a negative value in case of an error. + * + **/ +int +gnutls_dh_get_group (gnutls_session_t session, + gnutls_datum_t * raw_gen, gnutls_datum_t * raw_prime) +{ + dh_info_st *dh; + int ret; + anon_auth_info_t anon_info; + cert_auth_info_t cert_info; + + switch (gnutls_auth_get_type (session)) + { + case GNUTLS_CRD_ANON: + anon_info = _gnutls_get_auth_info (session); + if (anon_info == NULL) + return GNUTLS_E_INTERNAL_ERROR; + dh = &anon_info->dh; + break; + case GNUTLS_CRD_CERTIFICATE: + cert_info = _gnutls_get_auth_info (session); + if (cert_info == NULL) + return GNUTLS_E_INTERNAL_ERROR; + dh = &cert_info->dh; + break; + default: + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = _gnutls_set_datum (raw_prime, dh->prime.data, dh->prime.size); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + ret = _gnutls_set_datum (raw_gen, dh->generator.data, dh->generator.size); + if (ret < 0) + { + gnutls_assert (); + _gnutls_free_datum (raw_prime); + return ret; + } + + return 0; +} + +/** + * gnutls_dh_get_pubkey - This function returns the peer's public key used in DH authentication + * @session: is a gnutls session + * @raw_key: will hold the public key. + * + * This function will return the peer's public key used in the last Diffie Hellman authentication. + * This function should be used for both anonymous and ephemeral diffie Hellman. + * The output parameters must be freed with gnutls_free(). + * + * Returns a negative value in case of an error. + * + **/ +int +gnutls_dh_get_pubkey (gnutls_session_t session, gnutls_datum_t * raw_key) +{ + dh_info_st *dh; + anon_auth_info_t anon_info; + cert_auth_info_t cert_info; + cert_auth_info_t psk_info; + + switch (gnutls_auth_get_type (session)) + { + case GNUTLS_CRD_ANON: + { + anon_info = _gnutls_get_auth_info (session); + if (anon_info == NULL) + return GNUTLS_E_INTERNAL_ERROR; + dh = &anon_info->dh; + break; + } + case GNUTLS_CRD_PSK: + { + psk_info = _gnutls_get_auth_info (session); + if (psk_info == NULL) + return GNUTLS_E_INTERNAL_ERROR; + dh = &psk_info->dh; + break; + } + case GNUTLS_CRD_CERTIFICATE: + { + + cert_info = _gnutls_get_auth_info (session); + if (cert_info == NULL) + return GNUTLS_E_INTERNAL_ERROR; + dh = &cert_info->dh; + break; + } + default: + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_set_datum (raw_key, dh->public_key.data, + dh->public_key.size); +} + +/** + * gnutls_rsa_export_get_pubkey - This function returns the peer's public key used in RSA-EXPORT authentication + * @session: is a gnutls session + * @exponent: will hold the exponent. + * @modulus: will hold the modulus. + * + * This function will return the peer's public key exponent and + * modulus used in the last RSA-EXPORT authentication. The output + * parameters must be freed with gnutls_free(). + * + * Returns a negative value in case of an error. + * + **/ +int +gnutls_rsa_export_get_pubkey (gnutls_session_t session, + gnutls_datum_t * exponent, + gnutls_datum_t * modulus) +{ + cert_auth_info_t info; + int ret; + + if (gnutls_auth_get_type (session) == GNUTLS_CRD_CERTIFICATE) + { + info = _gnutls_get_auth_info (session); + if (info == NULL) + return GNUTLS_E_INTERNAL_ERROR; + + ret = _gnutls_set_datum (modulus, info->rsa_export.modulus.data, + info->rsa_export.modulus.size); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + ret = _gnutls_set_datum (exponent, info->rsa_export.exponent.data, + info->rsa_export.exponent.size); + if (ret < 0) + { + gnutls_assert (); + _gnutls_free_datum (modulus); + return ret; + } + + return 0; + } + + return GNUTLS_E_INVALID_REQUEST; +} + +/** + * gnutls_dh_get_secret_bits - This function returns the bits used in DH authentication + * @session: is a gnutls session + * + * This function will return the bits used in the last Diffie Hellman authentication + * with the peer. Should be used for both anonymous and ephemeral diffie Hellman. + * Returns a negative value in case of an error. + * + **/ +int +gnutls_dh_get_secret_bits (gnutls_session_t session) +{ + switch (gnutls_auth_get_type (session)) + { + case GNUTLS_CRD_ANON: + { + anon_auth_info_t info; + + info = _gnutls_get_auth_info (session); + if (info == NULL) + return GNUTLS_E_INTERNAL_ERROR; + return info->dh.secret_bits; + } + case GNUTLS_CRD_CERTIFICATE: + { + cert_auth_info_t info; + + info = _gnutls_get_auth_info (session); + if (info == NULL) + return GNUTLS_E_INTERNAL_ERROR; + + return info->dh.secret_bits; + } + default: + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } +} + +/** + * gnutls_dh_get_prime_bits - This function returns the bits used in DH authentication + * @session: is a gnutls session + * + * This function will return the bits of the prime used in the last Diffie Hellman authentication + * with the peer. Should be used for both anonymous and ephemeral diffie Hellman. + * Returns a negative value in case of an error. + * + **/ +int +gnutls_dh_get_prime_bits (gnutls_session_t session) +{ + dh_info_st *dh; + + switch (gnutls_auth_get_type (session)) + { + case GNUTLS_CRD_ANON: + { + anon_auth_info_t info; + + info = _gnutls_get_auth_info (session); + if (info == NULL) + return GNUTLS_E_INTERNAL_ERROR; + dh = &info->dh; + break; + } + case GNUTLS_CRD_CERTIFICATE: + { + cert_auth_info_t info; + + info = _gnutls_get_auth_info (session); + if (info == NULL) + return GNUTLS_E_INTERNAL_ERROR; + + dh = &info->dh; + break; + } + default: + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + return (dh->prime.size) * 8; + +} + +/** + * gnutls_rsa_export_get_modulus_bits - This function returns the bits used in RSA-export key exchange + * @session: is a gnutls session + * + * This function will return the bits used in the last RSA-EXPORT key exchange + * with the peer. + * Returns a negative value in case of an error. + * + **/ +int +gnutls_rsa_export_get_modulus_bits (gnutls_session_t session) +{ + cert_auth_info_t info; + + info = _gnutls_get_auth_info (session); + if (info == NULL) + return GNUTLS_E_INTERNAL_ERROR; + + return info->rsa_export.modulus.size * 8; +} + +/** + * gnutls_dh_get_peers_public_bits - This function returns the bits used in DH authentication + * @session: is a gnutls session + * + * This function will return the bits used in the last Diffie Hellman authentication + * with the peer. Should be used for both anonymous and ephemeral diffie Hellman. + * Returns a negative value in case of an error. + * + **/ +int +gnutls_dh_get_peers_public_bits (gnutls_session_t session) +{ + dh_info_st *dh; + + switch (gnutls_auth_get_type (session)) + { + case GNUTLS_CRD_ANON: + { + anon_auth_info_t info; + + info = _gnutls_get_auth_info (session); + if (info == NULL) + return GNUTLS_E_INTERNAL_ERROR; + + dh = &info->dh; + break; + } + case GNUTLS_CRD_CERTIFICATE: + { + cert_auth_info_t info; + + info = _gnutls_get_auth_info (session); + if (info == NULL) + return GNUTLS_E_INTERNAL_ERROR; + + dh = &info->dh; + break; + } + default: + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + return dh->public_key.size * 8; + +} + +/* CERTIFICATE STUFF */ + +/** + * gnutls_certificate_get_ours - This function returns the raw certificate sent in the last handshake + * @session: is a gnutls session + * + * This function will return the certificate as sent to the peer, + * in the last handshake. These certificates are in raw format. + * In X.509 this is a certificate list. In OpenPGP this is a single + * certificate. + * Returns NULL in case of an error, or if no certificate was used. + * + **/ +const gnutls_datum_t * +gnutls_certificate_get_ours (gnutls_session_t session) +{ + gnutls_certificate_credentials_t cred; + + CHECK_AUTH (GNUTLS_CRD_CERTIFICATE, NULL); + + cred + = (gnutls_certificate_credentials_t) _gnutls_get_cred (session->key, + GNUTLS_CRD_CERTIFICATE, + NULL); + if (cred == NULL || cred->cert_list == NULL) + { + gnutls_assert (); + return NULL; + } + + if (session->internals.selected_cert_list == NULL) + return NULL; + + return &session->internals.selected_cert_list[0].raw; +} + +/** + * gnutls_certificate_get_peers - This function returns the peer's raw certificate + * @session: is a gnutls session + * @list_size: is the length of the certificate list + * + * This function will return the peer's raw certificate (chain) as + * sent by the peer. These certificates are in raw format (DER encoded + * for X.509). In case of a X.509 then a certificate list may be present. + * The first certificate in the list is the peer's certificate, + * following the issuer's certificate, then the issuer's issuer etc. + * + * In case of OpenPGP keys a single key will be returned + * in raw format. + * + * Returns NULL in case of an error, or if no certificate was sent. + * + **/ +const gnutls_datum_t * +gnutls_certificate_get_peers (gnutls_session_t + session, unsigned int *list_size) +{ + cert_auth_info_t info; + + CHECK_AUTH (GNUTLS_CRD_CERTIFICATE, NULL); + + info = _gnutls_get_auth_info (session); + if (info == NULL) + return NULL; + + *list_size = info->ncerts; + return info->raw_certificate_list; +} + +/** + * gnutls_certificate_client_get_request_status - This function returns the certificate request status + * @session: is a gnutls session + * + * This function will return 0 if the peer (server) did not request client + * authentication or 1 otherwise. + * Returns a negative value in case of an error. + * + **/ +int +gnutls_certificate_client_get_request_status (gnutls_session_t session) +{ + cert_auth_info_t info; + + CHECK_AUTH (GNUTLS_CRD_CERTIFICATE, 0); + + info = _gnutls_get_auth_info (session); + if (info == NULL) + return GNUTLS_E_INTERNAL_ERROR; + return info->certificate_requested; +} + +/** + * gnutls_fingerprint - This function calculates the fingerprint of the given data + * @algo: is a digest algorithm + * @data: is the data + * @result: is the place where the result will be copied (may be null). + * @result_size: should hold the size of the result. The actual size + * of the returned result will also be copied there. + * + * This function will calculate a fingerprint (actually a hash), of the + * given data. The result is not printable data. You should convert it + * to hex, or to something else printable. + * + * This is the usual way to calculate a fingerprint of an X.509 + * DER encoded certificate. Note however that the fingerprint + * of an OpenPGP is not just a hash and cannot be calculated with + * this function. + * + * Returns a negative value in case of an error. + * + **/ +int +gnutls_fingerprint (gnutls_digest_algorithm_t algo, + const gnutls_datum_t * data, + void *result, size_t * result_size) +{ + GNUTLS_HASH_HANDLE td; + int hash_len = _gnutls_hash_get_algo_len (HASH2MAC (algo)); + + if (hash_len < 0 || (unsigned) hash_len > *result_size || result == NULL) + { + *result_size = hash_len; + return GNUTLS_E_SHORT_MEMORY_BUFFER; + } + *result_size = hash_len; + + if (result) + { + td = _gnutls_hash_init (HASH2MAC (algo)); + if (td == NULL) + return GNUTLS_E_HASH_FAILED; + + _gnutls_hash (td, data->data, data->size); + + _gnutls_hash_deinit (td, result); + } + + return 0; +} + +/** + * gnutls_certificate_set_dh_params - This function will set the DH parameters for a server to use + * @res: is a gnutls_certificate_credentials_t structure + * @dh_params: is a structure that holds diffie hellman parameters. + * + * This function will set the diffie hellman parameters for a + * certificate server to use. These parameters will be used in + * Ephemeral Diffie Hellman cipher suites. Note that only a pointer + * to the parameters are stored in the certificate handle, so if you + * deallocate the parameters before the certificate is deallocated, + * you must change the parameters stored in the certificate first. + * + **/ +void +gnutls_certificate_set_dh_params (gnutls_certificate_credentials_t res, + gnutls_dh_params_t dh_params) +{ + res->dh_params = dh_params; +} + +/** + * gnutls_certificate_set_params_function - This function will set the DH or RSA parameters callback + * @res: is a gnutls_certificate_credentials_t structure + * @func: is the function to be called + * + * This function will set a callback in order for the server to get the + * diffie hellman or RSA parameters for certificate authentication. The callback + * should return zero on success. + * + **/ +void +gnutls_certificate_set_params_function (gnutls_certificate_credentials_t res, + gnutls_params_function * func) +{ + res->params_func = func; +} + +/** + * gnutls_certificate_set_verify_flags - This function will set the flags to be used at certificate verification + * @res: is a gnutls_certificate_credentials_t structure + * @flags: are the flags + * + * This function will set the flags to be used at verification of the + * certificates. Flags must be OR of the + * #gnutls_certificate_verify_flags enumerations. + * + **/ +void +gnutls_certificate_set_verify_flags (gnutls_certificate_credentials_t + res, unsigned int flags) +{ + res->verify_flags = flags; +} + +/** + * gnutls_certificate_set_verify_limits - This function will set the upper limits to be used at certificate verification + * @res: is a gnutls_certificate_credentials structure + * @max_bits: is the number of bits of an acceptable certificate (default 8200) + * @max_depth: is maximum depth of the verification of a certificate chain (default 5) + * + * This function will set some upper limits for the default verification function, + * gnutls_certificate_verify_peers2(), to avoid denial of service attacks. + * You can set them to zero to disable limits. + * + **/ +void +gnutls_certificate_set_verify_limits (gnutls_certificate_credentials_t + res, + unsigned int max_bits, + unsigned int max_depth) +{ + res->verify_depth = max_depth; + res->verify_bits = max_bits; +} + +/** + * gnutls_certificate_set_rsa_export_params - This function will set the RSA parameters for a server to use + * @res: is a gnutls_certificate_credentials_t structure + * @rsa_params: is a structure that holds temporary RSA parameters. + * + * This function will set the temporary RSA parameters for a certificate + * server to use. These parameters will be used in RSA-EXPORT + * cipher suites. + * + **/ +void +gnutls_certificate_set_rsa_export_params (gnutls_certificate_credentials_t + res, gnutls_rsa_params_t rsa_params) +{ + res->rsa_params = rsa_params; +} + +/** + * gnutls_anon_set_params_function - This function will set the DH or RSA parameters callback + * @res: is a gnutls_anon_server_credentials_t structure + * @func: is the function to be called + * + * This function will set a callback in order for the server to get the + * diffie hellman or RSA parameters for anonymous authentication. The callback + * should return zero on success. + * + **/ +void +gnutls_anon_set_params_function (gnutls_anon_server_credentials_t res, + gnutls_params_function * func) +{ + res->params_func = func; +} diff --git a/src/daemon/https/tls/gnutls_v2_compat.c b/src/daemon/https/tls/gnutls_v2_compat.c new file mode 100644 index 00000000..ecf8c936 --- /dev/null +++ b/src/daemon/https/tls/gnutls_v2_compat.c @@ -0,0 +1,259 @@ +/* + * Copyright (C) 2001, 2004, 2005, 2006 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +/* Functions to parse the SSLv2.0 hello message. + */ + +#include "gnutls_int.h" +#include "gnutls_errors.h" +#include "gnutls_dh.h" +#include "debug.h" +#include "gnutls_algorithms.h" +#include "gnutls_compress.h" +#include "gnutls_cipher.h" +#include "gnutls_buffers.h" +#include "gnutls_kx.h" +#include "gnutls_handshake.h" +#include "gnutls_num.h" +#include "gnutls_hash_int.h" +#include "gnutls_db.h" +#include "gnutls_extensions.h" +#include "gnutls_auth_int.h" + +/* This selects the best supported ciphersuite from the ones provided */ +static int +_gnutls_handshake_select_v2_suite (gnutls_session_t session, + opaque * data, int datalen) +{ + int i, j, ret; + opaque *_data; + int _datalen; + + _gnutls_handshake_log ("HSK[%x]: Parsing a version 2.0 client hello.\n", + session); + + _data = gnutls_malloc (datalen); + if (_data == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + if (datalen % 3 != 0) + { + gnutls_assert (); + return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; + } + + i = _datalen = 0; + for (j = 0; j < datalen; j += 3) + { + if (data[j] == 0) + { + memcpy (&_data[i], &data[j + 1], 2); + i += 2; + _datalen += 2; + } + } + + ret = _gnutls_server_select_suite (session, _data, _datalen); + gnutls_free (_data); + + return ret; + +} + + +/* Read a v2 client hello. Some browsers still use that beast! + * However they set their version to 3.0 or 3.1. + */ +int +_gnutls_read_client_hello_v2 (gnutls_session_t session, opaque * data, + int datalen) +{ + uint16_t session_id_len = 0; + int pos = 0; + int ret = 0; + uint16_t sizeOfSuites; + gnutls_protocol_t adv_version; + opaque rnd[TLS_RANDOM_SIZE]; + int len = datalen; + int err; + uint16_t challenge; + opaque session_id[TLS_MAX_SESSION_ID_SIZE]; + + /* we only want to get here once - only in client hello */ + session->internals.v2_hello = 0; + + DECR_LEN (len, 2); + + _gnutls_handshake_log + ("HSK[%x]: SSL 2.0 Hello: Client's version: %d.%d\n", session, + data[pos], data[pos + 1]); + + set_adv_version (session, data[pos], data[pos + 1]); + + adv_version = _gnutls_version_get (data[pos], data[pos + 1]); + + ret = _gnutls_negotiate_version (session, adv_version); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + pos += 2; + + /* Read uint16_t cipher_spec_length */ + DECR_LEN (len, 2); + sizeOfSuites = _gnutls_read_uint16 (&data[pos]); + pos += 2; + + /* read session id length */ + DECR_LEN (len, 2); + session_id_len = _gnutls_read_uint16 (&data[pos]); + pos += 2; + + if (session_id_len > TLS_MAX_SESSION_ID_SIZE) + { + gnutls_assert (); + return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; + } + + /* read challenge length */ + DECR_LEN (len, 2); + challenge = _gnutls_read_uint16 (&data[pos]); + pos += 2; + + if (challenge < 16 || challenge > TLS_RANDOM_SIZE) + { + gnutls_assert (); + return GNUTLS_E_UNSUPPORTED_VERSION_PACKET; + } + + /* call the user hello callback + */ + ret = _gnutls_user_hello_func (session, adv_version); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + /* find an appropriate cipher suite */ + + DECR_LEN (len, sizeOfSuites); + ret = _gnutls_handshake_select_v2_suite (session, &data[pos], sizeOfSuites); + + pos += sizeOfSuites; + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + /* check if the credentials (username, public key etc.) are ok + */ + if (_gnutls_get_kx_cred + (session, + _gnutls_cipher_suite_get_kx_algo (&session->security_parameters. + current_cipher_suite), + &err) == NULL && err != 0) + { + gnutls_assert (); + return GNUTLS_E_INSUFFICIENT_CREDENTIALS; + } + + /* set the mod_auth_st to the appropriate struct + * according to the KX algorithm. This is needed since all the + * handshake functions are read from there; + */ + session->internals.auth_struct = + _gnutls_kx_auth_struct (_gnutls_cipher_suite_get_kx_algo + (&session->security_parameters. + current_cipher_suite)); + if (session->internals.auth_struct == NULL) + { + + _gnutls_handshake_log + ("HSK[%x]: SSL 2.0 Hello: Cannot find the appropriate handler for the KX algorithm\n", + session); + + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + + + /* read random new values -skip session id for now */ + DECR_LEN (len, session_id_len); /* skip session id for now */ + memcpy (session_id, &data[pos], session_id_len); + pos += session_id_len; + + DECR_LEN (len, challenge); + memset (rnd, 0, TLS_RANDOM_SIZE); + + memcpy (&rnd[TLS_RANDOM_SIZE - challenge], &data[pos], challenge); + + _gnutls_set_client_random (session, rnd); + + /* generate server random value */ + + _gnutls_tls_create_random (rnd); + _gnutls_set_server_random (session, rnd); + + session->security_parameters.timestamp = time (NULL); + + + /* RESUME SESSION */ + + DECR_LEN (len, session_id_len); + ret = _gnutls_server_restore_session (session, session_id, session_id_len); + + if (ret == 0) + { /* resumed! */ + /* get the new random values */ + memcpy (session->internals.resumed_security_parameters. + server_random, session->security_parameters.server_random, + TLS_RANDOM_SIZE); + memcpy (session->internals.resumed_security_parameters. + client_random, session->security_parameters.client_random, + TLS_RANDOM_SIZE); + + session->internals.resumed = RESUME_TRUE; + return 0; + } + else + { + _gnutls_generate_session_id (session->security_parameters. + session_id, + &session->security_parameters. + session_id_size); + session->internals.resumed = RESUME_FALSE; + } + + session->internals.compression_method = GNUTLS_COMP_NULL; + + return 0; +} diff --git a/src/daemon/https/tls/gnutls_v2_compat.h b/src/daemon/https/tls/gnutls_v2_compat.h new file mode 100644 index 00000000..59ee6130 --- /dev/null +++ b/src/daemon/https/tls/gnutls_v2_compat.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +int _gnutls_read_client_hello_v2 (gnutls_session_t session, opaque * data, + int datalen); diff --git a/src/daemon/https/tls/gnutls_x509.c b/src/daemon/https/tls/gnutls_x509.c new file mode 100644 index 00000000..29d20957 --- /dev/null +++ b/src/daemon/https/tls/gnutls_x509.c @@ -0,0 +1,1991 @@ +/* + * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +#include +#include "gnutls_auth_int.h" +#include "gnutls_errors.h" +#include +#include +#include "gnutls_dh.h" +#include "gnutls_num.h" +#include "libtasn1.h" +#include "gnutls_datum.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* x509 */ +#include "common.h" +#include "x509.h" +#include "verify.h" +#include "mpi.h" +#include "pkcs7.h" +#include "privkey.h" + + +/* + * some x509 certificate parsing functions. + */ + +/* Check if the number of bits of the key in the certificate + * is unacceptable. + */ +inline static int +check_bits (gnutls_x509_crt_t crt, unsigned int max_bits) +{ + int ret; + unsigned int bits; + + ret = gnutls_x509_crt_get_pk_algorithm (crt, &bits); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + if (bits > max_bits && max_bits > 0) + { + gnutls_assert (); + return GNUTLS_E_CONSTRAINT_ERROR; + } + + return 0; +} + + +#define CLEAR_CERTS for(x=0;xkey, GNUTLS_CRD_CERTIFICATE, NULL); + if (cred == NULL) + { + gnutls_assert (); + return GNUTLS_E_INSUFFICIENT_CREDENTIALS; + } + + if (info->raw_certificate_list == NULL || info->ncerts == 0) + return GNUTLS_E_NO_CERTIFICATE_FOUND; + + if (info->ncerts > cred->verify_depth && cred->verify_depth > 0) + { + gnutls_assert (); + return GNUTLS_E_CONSTRAINT_ERROR; + } + + /* generate a list of gnutls_certs based on the auth info + * raw certs. + */ + peer_certificate_list_size = info->ncerts; + peer_certificate_list = + gnutls_calloc (1, + peer_certificate_list_size * sizeof (gnutls_x509_crt_t)); + if (peer_certificate_list == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + for (i = 0; i < peer_certificate_list_size; i++) + { + ret = gnutls_x509_crt_init (&peer_certificate_list[i]); + if (ret < 0) + { + gnutls_assert (); + CLEAR_CERTS; + return ret; + } + + ret = + gnutls_x509_crt_import (peer_certificate_list[i], + &info->raw_certificate_list[i], + GNUTLS_X509_FMT_DER); + if (ret < 0) + { + gnutls_assert (); + CLEAR_CERTS; + return ret; + } + + ret = check_bits (peer_certificate_list[i], cred->verify_bits); + if (ret < 0) + { + gnutls_assert (); + CLEAR_CERTS; + return ret; + } + + } + + /* Verify certificate + */ + ret = + gnutls_x509_crt_list_verify (peer_certificate_list, + peer_certificate_list_size, + cred->x509_ca_list, cred->x509_ncas, + cred->x509_crl_list, cred->x509_ncrls, + cred->verify_flags, status); + + CLEAR_CERTS; + + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + return 0; +} + +/* + * Read certificates and private keys, from files, memory etc. + */ + +/* returns error if the certificate has different algorithm than + * the given key parameters. + */ +static int +_gnutls_check_key_cert_match (gnutls_certificate_credentials_t res) +{ + gnutls_datum_t cid; + gnutls_datum_t kid; + unsigned pk = res->cert_list[res->ncerts - 1][0].subject_pk_algorithm; + + if (res->pkey[res->ncerts - 1].pk_algorithm != pk) + { + gnutls_assert (); + return GNUTLS_E_CERTIFICATE_KEY_MISMATCH; + } + + _gnutls_x509_write_rsa_params (res->pkey[res->ncerts - 1].params, + res->pkey[res->ncerts - + 1].params_size, &kid); + + + _gnutls_x509_write_rsa_params (res->cert_list[res->ncerts - 1][0]. + params, + res->cert_list[res->ncerts - + 1][0].params_size, &cid); + + if (cid.size != kid.size) + { + gnutls_assert (); + _gnutls_free_datum (&kid); + _gnutls_free_datum (&cid); + return GNUTLS_E_CERTIFICATE_KEY_MISMATCH; + } + + if (memcmp (kid.data, cid.data, kid.size) != 0) + { + gnutls_assert (); + _gnutls_free_datum (&kid); + _gnutls_free_datum (&cid); + return GNUTLS_E_CERTIFICATE_KEY_MISMATCH; + } + + _gnutls_free_datum (&kid); + _gnutls_free_datum (&cid); + return 0; +} + +/* Reads a DER encoded certificate list from memory and stores it to + * a gnutls_cert structure. This is only called if PKCS7 read fails. + * returns the number of certificates parsed (1) + */ +static int +parse_crt_mem (gnutls_cert ** cert_list, unsigned *ncerts, + gnutls_x509_crt_t cert) +{ + int i; + int ret; + + i = *ncerts + 1; + + *cert_list = + (gnutls_cert *) gnutls_realloc_fast (*cert_list, + i * sizeof (gnutls_cert)); + + if (*cert_list == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + ret = _gnutls_x509_crt_to_gcert (&cert_list[0][i - 1], cert, 0); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + *ncerts = i; + + return 1; /* one certificate parsed */ +} + +/* Reads a DER encoded certificate list from memory and stores it to + * a gnutls_cert structure. This is only called if PKCS7 read fails. + * returns the number of certificates parsed (1) + */ +static int +parse_der_cert_mem (gnutls_cert ** cert_list, unsigned *ncerts, + const void *input_cert, int input_cert_size) +{ + gnutls_datum_t tmp; + gnutls_x509_crt_t cert; + int ret; + + ret = gnutls_x509_crt_init (&cert); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + tmp.data = (opaque *) input_cert; + tmp.size = input_cert_size; + + ret = gnutls_x509_crt_import (cert, &tmp, GNUTLS_X509_FMT_DER); + if (ret < 0) + { + gnutls_assert (); + gnutls_x509_crt_deinit (cert); + return ret; + } + + ret = parse_crt_mem (cert_list, ncerts, cert); + gnutls_x509_crt_deinit (cert); + + return ret; +} + +#define CERT_PEM 1 + + +/* Reads a PKCS7 base64 encoded certificate list from memory and stores it to + * a gnutls_cert structure. + * returns the number of certificate parsed + */ +static int +parse_pkcs7_cert_mem (gnutls_cert ** cert_list, unsigned *ncerts, const + void *input_cert, int input_cert_size, int flags) +{ +#ifdef ENABLE_PKI + int i, j, count; + gnutls_datum_t tmp, tmp2; + int ret; + opaque *pcert = NULL; + size_t pcert_size; + gnutls_pkcs7_t pkcs7; + + ret = gnutls_pkcs7_init (&pkcs7); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + if (flags & CERT_PEM) + ret = gnutls_pkcs7_import (pkcs7, &tmp, GNUTLS_X509_FMT_PEM); + else + ret = gnutls_pkcs7_import (pkcs7, &tmp, GNUTLS_X509_FMT_DER); + if (ret < 0) + { + /* if we failed to read the structure, + * then just try to decode a plain DER + * certificate. + */ + gnutls_assert (); + gnutls_pkcs7_deinit (pkcs7); +#endif + return parse_der_cert_mem (cert_list, ncerts, + input_cert, input_cert_size); +#ifdef ENABLE_PKI + } + + i = *ncerts + 1; + + /* tmp now contains the decoded certificate list */ + tmp.data = (opaque *) input_cert; + tmp.size = input_cert_size; + + ret = gnutls_pkcs7_get_crt_count (pkcs7); + + if (ret < 0) + { + gnutls_assert (); + gnutls_pkcs7_deinit (pkcs7); + return ret; + } + count = ret; + + j = count - 1; + do + { + pcert_size = 0; + ret = gnutls_pkcs7_get_crt_raw (pkcs7, j, NULL, &pcert_size); + if (ret != GNUTLS_E_MEMORY_ERROR) + { + count--; + continue; + } + + pcert = gnutls_malloc (pcert_size); + if (ret == GNUTLS_E_MEMORY_ERROR) + { + gnutls_assert (); + count--; + continue; + } + + /* read the certificate + */ + ret = gnutls_pkcs7_get_crt_raw (pkcs7, j, pcert, &pcert_size); + + j--; + + if (ret >= 0) + { + *cert_list = + (gnutls_cert *) gnutls_realloc_fast (*cert_list, + i * sizeof (gnutls_cert)); + + if (*cert_list == NULL) + { + gnutls_assert (); + gnutls_free (pcert); + return GNUTLS_E_MEMORY_ERROR; + } + + tmp2.data = pcert; + tmp2.size = pcert_size; + + ret = + _gnutls_x509_raw_cert_to_gcert (&cert_list[0][i - 1], &tmp2, 0); + + if (ret < 0) + { + gnutls_assert (); + gnutls_pkcs7_deinit (pkcs7); + gnutls_free (pcert); + return ret; + } + + i++; + } + + gnutls_free (pcert); + + } + while (ret >= 0 && j >= 0); + + *ncerts = i - 1; + + gnutls_pkcs7_deinit (pkcs7); + return count; +#endif +} + +/* Reads a base64 encoded certificate list from memory and stores it to + * a gnutls_cert structure. Returns the number of certificate parsed. + */ +static int +parse_pem_cert_mem (gnutls_cert ** cert_list, unsigned *ncerts, + const char *input_cert, int input_cert_size) +{ + int size, siz2, i; + const char *ptr; + opaque *ptr2; + gnutls_datum_t tmp; + int ret, count; + +#ifdef ENABLE_PKI + if ((ptr = memmem (input_cert, input_cert_size, + PEM_PKCS7_SEP, sizeof (PEM_PKCS7_SEP) - 1)) != NULL) + { + size = strlen (ptr); + + ret = parse_pkcs7_cert_mem (cert_list, ncerts, ptr, size, CERT_PEM); + + return ret; + } +#endif + + /* move to the certificate + */ + ptr = memmem (input_cert, input_cert_size, + PEM_CERT_SEP, sizeof (PEM_CERT_SEP) - 1); + if (ptr == NULL) + ptr = memmem (input_cert, input_cert_size, + PEM_CERT_SEP2, sizeof (PEM_CERT_SEP2) - 1); + + if (ptr == NULL) + { + gnutls_assert (); + return GNUTLS_E_BASE64_DECODING_ERROR; + } + size = input_cert_size - (ptr - input_cert); + + i = *ncerts + 1; + count = 0; + + do + { + + siz2 = _gnutls_fbase64_decode (NULL, ptr, size, &ptr2); + + if (siz2 < 0) + { + gnutls_assert (); + return GNUTLS_E_BASE64_DECODING_ERROR; + } + + *cert_list = + (gnutls_cert *) gnutls_realloc_fast (*cert_list, + i * sizeof (gnutls_cert)); + + if (*cert_list == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + tmp.data = ptr2; + tmp.size = siz2; + + ret = _gnutls_x509_raw_cert_to_gcert (&cert_list[0][i - 1], &tmp, 0); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + _gnutls_free_datum (&tmp); /* free ptr2 */ + + /* now we move ptr after the pem header + */ + ptr++; + /* find the next certificate (if any) + */ + size = input_cert_size - (ptr - input_cert); + + if (size > 0) + { + char *ptr3; + + ptr3 = memmem (ptr, size, PEM_CERT_SEP, sizeof (PEM_CERT_SEP) - 1); + if (ptr3 == NULL) + ptr3 = memmem (ptr, size, PEM_CERT_SEP2, + sizeof (PEM_CERT_SEP2) - 1); + + ptr = ptr3; + } + else + ptr = NULL; + + i++; + count++; + + } + while (ptr != NULL); + + *ncerts = i - 1; + + return count; +} + + + +/* Reads a DER or PEM certificate from memory + */ +static int +read_cert_mem (gnutls_certificate_credentials_t res, const void *cert, + int cert_size, gnutls_x509_crt_fmt_t type) +{ + int ret; + + /* allocate space for the certificate to add + */ + res->cert_list = gnutls_realloc_fast (res->cert_list, + (1 + + res->ncerts) * + sizeof (gnutls_cert *)); + if (res->cert_list == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + res->cert_list_length = gnutls_realloc_fast (res->cert_list_length, + (1 + + res->ncerts) * sizeof (int)); + if (res->cert_list_length == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + res->cert_list[res->ncerts] = NULL; /* for realloc */ + res->cert_list_length[res->ncerts] = 0; + + if (type == GNUTLS_X509_FMT_DER) + ret = parse_pkcs7_cert_mem (&res->cert_list[res->ncerts], + &res->cert_list_length[res->ncerts], + cert, cert_size, 0); + else + ret = + parse_pem_cert_mem (&res->cert_list[res->ncerts], + &res->cert_list_length[res->ncerts], cert, + cert_size); + + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + return ret; +} + + +int +_gnutls_x509_privkey_to_gkey (gnutls_privkey * dest, + gnutls_x509_privkey_t src) +{ + int i, ret; + + memset (dest, 0, sizeof (gnutls_privkey)); + + for (i = 0; i < src->params_size; i++) + { + dest->params[i] = _gnutls_mpi_copy (src->params[i]); + if (dest->params[i] == NULL) + { + gnutls_assert (); + ret = GNUTLS_E_MEMORY_ERROR; + goto cleanup; + } + } + + dest->pk_algorithm = src->pk_algorithm; + dest->params_size = src->params_size; + + return 0; + +cleanup: + + for (i = 0; i < src->params_size; i++) + { + _gnutls_mpi_release (&dest->params[i]); + } + return ret; +} + +void +_gnutls_gkey_deinit (gnutls_privkey * key) +{ + int i; + if (key == NULL) + return; + + for (i = 0; i < key->params_size; i++) + { + _gnutls_mpi_release (&key->params[i]); + } +} + +int +_gnutls_x509_raw_privkey_to_gkey (gnutls_privkey * privkey, + const gnutls_datum_t * raw_key, + gnutls_x509_crt_fmt_t type) +{ + gnutls_x509_privkey_t tmpkey; + int ret; + + ret = gnutls_x509_privkey_init (&tmpkey); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + ret = gnutls_x509_privkey_import (tmpkey, raw_key, type); + + /* If normal key decoding doesn't work try decoding a plain PKCS #8 key */ + if (ret < 0) + ret = + gnutls_x509_privkey_import_pkcs8 (tmpkey, raw_key, type, NULL, + GNUTLS_PKCS_PLAIN); + + if (ret < 0) + { + gnutls_assert (); + gnutls_x509_privkey_deinit (tmpkey); + return ret; + } + + ret = _gnutls_x509_privkey_to_gkey (privkey, tmpkey); + if (ret < 0) + { + gnutls_assert (); + gnutls_x509_privkey_deinit (tmpkey); + return ret; + } + + gnutls_x509_privkey_deinit (tmpkey); + + return 0; +} + +/* Reads a PEM encoded PKCS-1 RSA/DSA private key from memory. Type + * indicates the certificate format. KEY can be NULL, to indicate + * that GnuTLS doesn't know the private key. + */ +static int +read_key_mem (gnutls_certificate_credentials_t res, + const void *key, int key_size, gnutls_x509_crt_fmt_t type) +{ + int ret; + gnutls_datum_t tmp; + + /* allocate space for the pkey list + */ + res->pkey = + gnutls_realloc_fast (res->pkey, + (res->ncerts + 1) * sizeof (gnutls_privkey)); + if (res->pkey == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + if (key) + { + tmp.data = (opaque *) key; + tmp.size = key_size; + + ret = + _gnutls_x509_raw_privkey_to_gkey (&res->pkey[res->ncerts], &tmp, + type); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + } + else + memset (&res->pkey[res->ncerts], 0, sizeof (gnutls_privkey)); + + return 0; +} + +/* Reads a certificate file + */ +static int +read_cert_file (gnutls_certificate_credentials_t res, + const char *certfile, gnutls_x509_crt_fmt_t type) +{ + int ret; + size_t size; + char *data = read_binary_file (certfile, &size); + + if (data == NULL) + { + gnutls_assert (); + return GNUTLS_E_FILE_ERROR; + } + + ret = read_cert_mem (res, data, size, type); + free (data); + + return ret; + +} + + + +/* Reads PKCS-1 RSA private key file or a DSA file (in the format openssl + * stores it). + */ +static int +read_key_file (gnutls_certificate_credentials_t res, + const char *keyfile, gnutls_x509_crt_fmt_t type) +{ + int ret; + size_t size; + char *data = read_binary_file (keyfile, &size); + + if (data == NULL) + { + gnutls_assert (); + return GNUTLS_E_FILE_ERROR; + } + + ret = read_key_mem (res, data, size, type); + free (data); + + return ret; +} + +/** + * gnutls_certificate_set_x509_key_mem - Used to set keys in a gnutls_certificate_credentials_t structure + * @res: is an #gnutls_certificate_credentials_t structure. + * @cert: contains a certificate list (path) for the specified private key + * @key: is the private key, or %NULL + * @type: is PEM or DER + * + * This function sets a certificate/private key pair in the + * gnutls_certificate_credentials_t structure. This function may be called + * more than once (in case multiple keys/certificates exist for the + * server). + * + * Currently are supported: RSA PKCS-1 encoded private keys, + * DSA private keys. + * + * DSA private keys are encoded the OpenSSL way, which is an ASN.1 + * DER sequence of 6 INTEGERs - version, p, q, g, pub, priv. + * + * Note that the keyUsage (2.5.29.15) PKIX extension in X.509 certificates + * is supported. This means that certificates intended for signing cannot + * be used for ciphersuites that require encryption. + * + * If the certificate and the private key are given in PEM encoding + * then the strings that hold their values must be null terminated. + * + * The @key may be %NULL if you are using a sign callback, see + * gnutls_sign_callback_set(). + * + * Returns: %GNUTLS_E_SUCCESS on success, or an error code. + **/ +int +gnutls_certificate_set_x509_key_mem (gnutls_certificate_credentials_t + res, const gnutls_datum_t * cert, + const gnutls_datum_t * key, + gnutls_x509_crt_fmt_t type) +{ + int ret; + + /* this should be first + */ + if ((ret = read_key_mem (res, key ? key->data : NULL, + key ? key->size : 0, type)) < 0) + return ret; + + if ((ret = read_cert_mem (res, cert->data, cert->size, type)) < 0) + return ret; + + res->ncerts++; + + if (key && (ret = _gnutls_check_key_cert_match (res)) < 0) + { + gnutls_assert (); + return ret; + } + + return 0; +} + +/** + * gnutls_certificate_set_x509_key - Used to set keys in a gnutls_certificate_credentials_t structure + * @res: is an #gnutls_certificate_credentials_t structure. + * @cert_list: contains a certificate list (path) for the specified private key + * @cert_list_size: holds the size of the certificate list + * @key: is a gnutls_x509_privkey_t key + * + * This function sets a certificate/private key pair in the + * gnutls_certificate_credentials_t structure. This function may be + * called more than once (in case multiple keys/certificates exist + * for the server). + * + * Returns: %GNUTLS_E_SUCCESS on success, or an error code. + **/ +int +gnutls_certificate_set_x509_key (gnutls_certificate_credentials_t res, + gnutls_x509_crt_t * cert_list, + int cert_list_size, + gnutls_x509_privkey_t key) +{ + int ret, i; + + /* this should be first + */ + + res->pkey = + gnutls_realloc_fast (res->pkey, + (res->ncerts + 1) * sizeof (gnutls_privkey)); + if (res->pkey == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + ret = _gnutls_x509_privkey_to_gkey (&res->pkey[res->ncerts], key); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + res->cert_list = gnutls_realloc_fast (res->cert_list, + (1 + + res->ncerts) * + sizeof (gnutls_cert *)); + if (res->cert_list == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + res->cert_list_length = gnutls_realloc_fast (res->cert_list_length, + (1 + + res->ncerts) * sizeof (int)); + if (res->cert_list_length == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + res->cert_list[res->ncerts] = NULL; /* for realloc */ + res->cert_list_length[res->ncerts] = 0; + + + for (i = 0; i < cert_list_size; i++) + { + ret = parse_crt_mem (&res->cert_list[res->ncerts], + &res->cert_list_length[res->ncerts], cert_list[i]); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + } + res->ncerts++; + + if ((ret = _gnutls_check_key_cert_match (res)) < 0) + { + gnutls_assert (); + return ret; + } + + return 0; +} + +/** + * gnutls_certificate_set_x509_key_file - Used to set keys in a gnutls_certificate_credentials_t structure + * @res: is an #gnutls_certificate_credentials_t structure. + * @CERTFILE: is a file that containing the certificate list (path) for + * the specified private key, in PKCS7 format, or a list of certificates + * @KEYFILE: is a file that contains the private key + * @type: is PEM or DER + * + * This function sets a certificate/private key pair in the + * gnutls_certificate_credentials_t structure. This function may be + * called more than once (in case multiple keys/certificates exist + * for the server). + * + * Currently only PKCS-1 encoded RSA and DSA private keys are accepted by + * this function. + * + * Returns: %GNUTLS_E_SUCCESS on success, or an error code. + **/ +int +gnutls_certificate_set_x509_key_file (gnutls_certificate_credentials_t + res, const char *CERTFILE, + const char *KEYFILE, + gnutls_x509_crt_fmt_t type) +{ + int ret; + + /* this should be first + */ + if ((ret = read_key_file (res, KEYFILE, type)) < 0) + return ret; + + if ((ret = read_cert_file (res, CERTFILE, type)) < 0) + return ret; + + res->ncerts++; + + if ((ret = _gnutls_check_key_cert_match (res)) < 0) + { + gnutls_assert (); + return ret; + } + + return 0; +} + +static int +generate_rdn_seq (gnutls_certificate_credentials_t res) +{ + gnutls_datum_t tmp; + int ret; + unsigned size, i; + opaque *pdata; + + /* Generate the RDN sequence + * This will be sent to clients when a certificate + * request message is sent. + */ + + /* FIXME: in case of a client it is not needed + * to do that. This would save time and memory. + * However we don't have that information available + * here. + */ + + size = 0; + for (i = 0; i < res->x509_ncas; i++) + { + if ((ret = gnutls_x509_crt_get_raw_dn (res->x509_ca_list[i], &tmp)) < 0) + { + gnutls_assert (); + return ret; + } + size += (2 + tmp.size); + _gnutls_free_datum (&tmp); + } + + if (res->x509_rdn_sequence.data != NULL) + gnutls_free (res->x509_rdn_sequence.data); + + res->x509_rdn_sequence.data = gnutls_malloc (size); + if (res->x509_rdn_sequence.data == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + res->x509_rdn_sequence.size = size; + + pdata = res->x509_rdn_sequence.data; + + for (i = 0; i < res->x509_ncas; i++) + { + if ((ret = gnutls_x509_crt_get_raw_dn (res->x509_ca_list[i], &tmp)) < 0) + { + _gnutls_free_datum (&res->x509_rdn_sequence); + gnutls_assert (); + return ret; + } + + _gnutls_write_datum16 (pdata, tmp); + pdata += (2 + tmp.size); + _gnutls_free_datum (&tmp); + } + + return 0; +} + + + + +/* Returns 0 if it's ok to use the gnutls_kx_algorithm_t with this + * certificate (uses the KeyUsage field). + */ +int +_gnutls_check_key_usage (const gnutls_cert * cert, gnutls_kx_algorithm_t alg) +{ + unsigned int key_usage = 0; + int encipher_type; + + if (cert == NULL) + { + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + if (_gnutls_map_kx_get_cred (alg, 1) == GNUTLS_CRD_CERTIFICATE || + _gnutls_map_kx_get_cred (alg, 0) == GNUTLS_CRD_CERTIFICATE) + { + + key_usage = cert->key_usage; + + encipher_type = _gnutls_kx_encipher_type (alg); + + if (key_usage != 0 && encipher_type != CIPHER_IGN) + { + /* If key_usage has been set in the certificate + */ + + if (encipher_type == CIPHER_ENCRYPT) + { + /* If the key exchange method requires an encipher + * type algorithm, and key's usage does not permit + * encipherment, then fail. + */ + if (!(key_usage & KEY_KEY_ENCIPHERMENT)) + { + gnutls_assert (); + return GNUTLS_E_KEY_USAGE_VIOLATION; + } + } + + if (encipher_type == CIPHER_SIGN) + { + /* The same as above, but for sign only keys + */ + if (!(key_usage & KEY_DIGITAL_SIGNATURE)) + { + gnutls_assert (); + return GNUTLS_E_KEY_USAGE_VIOLATION; + } + } + } + } + return 0; +} + + + +static int +parse_pem_ca_mem (gnutls_x509_crt_t ** cert_list, unsigned *ncerts, + const opaque * input_cert, int input_cert_size) +{ + int i, size; + const opaque *ptr; + gnutls_datum_t tmp; + int ret, count; + + /* move to the certificate + */ + ptr = memmem (input_cert, input_cert_size, + PEM_CERT_SEP, sizeof (PEM_CERT_SEP) - 1); + if (ptr == NULL) + ptr = memmem (input_cert, input_cert_size, + PEM_CERT_SEP2, sizeof (PEM_CERT_SEP2) - 1); + + if (ptr == NULL) + { + gnutls_assert (); + return GNUTLS_E_BASE64_DECODING_ERROR; + } + size = input_cert_size - (ptr - input_cert); + + i = *ncerts + 1; + count = 0; + + do + { + + *cert_list = + (gnutls_x509_crt_t *) gnutls_realloc_fast (*cert_list, + i * + sizeof + (gnutls_x509_crt_t)); + + if (*cert_list == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + ret = gnutls_x509_crt_init (&cert_list[0][i - 1]); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + tmp.data = (opaque *) ptr; + tmp.size = size; + + ret = + gnutls_x509_crt_import (cert_list[0][i - 1], + &tmp, GNUTLS_X509_FMT_PEM); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + /* now we move ptr after the pem header + */ + ptr++; + size--; + /* find the next certificate (if any) + */ + + if (size > 0) + { + char *ptr3; + + ptr3 = memmem (ptr, size, PEM_CERT_SEP, sizeof (PEM_CERT_SEP) - 1); + if (ptr3 == NULL) + ptr3 = memmem (ptr, size, + PEM_CERT_SEP2, sizeof (PEM_CERT_SEP2) - 1); + + ptr = ptr3; + size = input_cert_size - (ptr - input_cert); + } + else + ptr = NULL; + + i++; + count++; + + } + while (ptr != NULL); + + *ncerts = i - 1; + + return count; +} + +/* Reads a DER encoded certificate list from memory and stores it to + * a gnutls_cert structure. This is only called if PKCS7 read fails. + * returns the number of certificates parsed (1) + */ +static int +parse_der_ca_mem (gnutls_x509_crt_t ** cert_list, unsigned *ncerts, + const void *input_cert, int input_cert_size) +{ + int i; + gnutls_datum_t tmp; + int ret; + + i = *ncerts + 1; + + *cert_list = + (gnutls_x509_crt_t *) gnutls_realloc_fast (*cert_list, + i * + sizeof (gnutls_x509_crt_t)); + + if (*cert_list == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + tmp.data = (opaque *) input_cert; + tmp.size = input_cert_size; + + ret = gnutls_x509_crt_init (&cert_list[0][i - 1]); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + ret = + gnutls_x509_crt_import (cert_list[0][i - 1], &tmp, GNUTLS_X509_FMT_DER); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + *ncerts = i; + + return 1; /* one certificate parsed */ +} + +/** + * gnutls_certificate_set_x509_trust_mem - Used to add trusted CAs in a gnutls_certificate_credentials_t structure + * @res: is an #gnutls_certificate_credentials_t structure. + * @ca: is a list of trusted CAs or a DER certificate + * @type: is DER or PEM + * + * This function adds the trusted CAs in order to verify client or + * server certificates. In case of a client this is not required to + * be called if the certificates are not verified using + * gnutls_certificate_verify_peers2(). This function may be called + * multiple times. + * + * In case of a server the CAs set here will be sent to the client if + * a certificate request is sent. This can be disabled using + * gnutls_certificate_send_x509_rdn_sequence(). + * + * Returns: the number of certificates processed or a negative value + * on error. + **/ +int +gnutls_certificate_set_x509_trust_mem (gnutls_certificate_credentials_t + res, const gnutls_datum_t * ca, + gnutls_x509_crt_fmt_t type) +{ + int ret, ret2; + + if (type == GNUTLS_X509_FMT_DER) + ret = parse_der_ca_mem (&res->x509_ca_list, &res->x509_ncas, + ca->data, ca->size); + else + ret = parse_pem_ca_mem (&res->x509_ca_list, &res->x509_ncas, + ca->data, ca->size); + + if ((ret2 = generate_rdn_seq (res)) < 0) + return ret2; + + return ret; +} + +/** + * gnutls_certificate_set_x509_trust - Used to add trusted CAs in a gnutls_certificate_credentials_t structure + * @res: is an #gnutls_certificate_credentials_t structure. + * @ca_list: is a list of trusted CAs + * @ca_list_size: holds the size of the CA list + * + * This function adds the trusted CAs in order to verify client + * or server certificates. In case of a client this is not required + * to be called if the certificates are not verified using + * gnutls_certificate_verify_peers2(). + * This function may be called multiple times. + * + * In case of a server the CAs set here will be sent to the client if + * a certificate request is sent. This can be disabled using + * gnutls_certificate_send_x509_rdn_sequence(). + * + * Returns: %GNUTLS_E_SUCCESS on success, or an error code. + **/ +int +gnutls_certificate_set_x509_trust (gnutls_certificate_credentials_t res, + gnutls_x509_crt_t * ca_list, + int ca_list_size) +{ + int ret, i, ret2; + + res->x509_ca_list = gnutls_realloc_fast (res->x509_ca_list, + (ca_list_size + + res->x509_ncas) * + sizeof (gnutls_x509_crt_t)); + if (res->x509_ca_list == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + for (i = 0; i < ca_list_size; i++) + { + ret = gnutls_x509_crt_init (&res->x509_ca_list[res->x509_ncas]); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + ret = _gnutls_x509_crt_cpy (res->x509_ca_list[res->x509_ncas], + ca_list[i]); + if (ret < 0) + { + gnutls_assert (); + gnutls_x509_crt_deinit (res->x509_ca_list[res->x509_ncas]); + return ret; + } + res->x509_ncas++; + } + + if ((ret2 = generate_rdn_seq (res)) < 0) + return ret2; + + return 0; +} + +/** + * gnutls_certificate_set_x509_trust_file - Used to add trusted CAs in a gnutls_certificate_credentials_t structure + * @res: is an #gnutls_certificate_credentials_t structure. + * @cafile: is a file containing the list of trusted CAs (DER or PEM list) + * @type: is PEM or DER + * + * This function adds the trusted CAs in order to verify client or + * server certificates. In case of a client this is not required to + * be called if the certificates are not verified using + * gnutls_certificate_verify_peers2(). This function may be called + * multiple times. + * + * In case of a server the names of the CAs set here will be sent to + * the client if a certificate request is sent. This can be disabled + * using gnutls_certificate_send_x509_rdn_sequence(). + * + * Returns: number of certificates processed, or a negative value on + * error. + **/ +int +gnutls_certificate_set_x509_trust_file (gnutls_certificate_credentials_t + res, const char *cafile, + gnutls_x509_crt_fmt_t type) +{ + int ret, ret2; + size_t size; + char *data = read_binary_file (cafile, &size); + + if (data == NULL) + { + gnutls_assert (); + return GNUTLS_E_FILE_ERROR; + } + + if (type == GNUTLS_X509_FMT_DER) + ret = parse_der_ca_mem (&res->x509_ca_list, &res->x509_ncas, data, size); + else + ret = parse_pem_ca_mem (&res->x509_ca_list, &res->x509_ncas, data, size); + + free (data); + + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + if ((ret2 = generate_rdn_seq (res)) < 0) + return ret2; + + return ret; +} + +#ifdef ENABLE_PKI + +static int +parse_pem_crl_mem (gnutls_x509_crl_t ** crl_list, unsigned *ncrls, + const opaque * input_crl, int input_crl_size) +{ + int size, i; + const opaque *ptr; + gnutls_datum_t tmp; + int ret, count; + + /* move to the certificate + */ + ptr = memmem (input_crl, input_crl_size, + PEM_CRL_SEP, sizeof (PEM_CRL_SEP) - 1); + if (ptr == NULL) + { + gnutls_assert (); + return GNUTLS_E_BASE64_DECODING_ERROR; + } + + size = input_crl_size - (ptr - input_crl); + + i = *ncrls + 1; + count = 0; + + do + { + + *crl_list = + (gnutls_x509_crl_t *) gnutls_realloc_fast (*crl_list, + i * + sizeof + (gnutls_x509_crl_t)); + + if (*crl_list == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + ret = gnutls_x509_crl_init (&crl_list[0][i - 1]); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + tmp.data = (char *) ptr; + tmp.size = size; + + ret = + gnutls_x509_crl_import (crl_list[0][i - 1], + &tmp, GNUTLS_X509_FMT_PEM); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + /* now we move ptr after the pem header + */ + ptr++; + /* find the next certificate (if any) + */ + + size = input_crl_size - (ptr - input_crl); + + if (size > 0) + ptr = memmem (ptr, size, PEM_CRL_SEP, sizeof (PEM_CRL_SEP) - 1); + else + ptr = NULL; + i++; + count++; + + } + while (ptr != NULL); + + *ncrls = i - 1; + + return count; +} + +/* Reads a DER encoded certificate list from memory and stores it to + * a gnutls_cert structure. This is only called if PKCS7 read fails. + * returns the number of certificates parsed (1) + */ +static int +parse_der_crl_mem (gnutls_x509_crl_t ** crl_list, unsigned *ncrls, + const void *input_crl, int input_crl_size) +{ + int i; + gnutls_datum_t tmp; + int ret; + + i = *ncrls + 1; + + *crl_list = + (gnutls_x509_crl_t *) gnutls_realloc_fast (*crl_list, + i * + sizeof (gnutls_x509_crl_t)); + + if (*crl_list == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + tmp.data = (opaque *) input_crl; + tmp.size = input_crl_size; + + ret = gnutls_x509_crl_init (&crl_list[0][i - 1]); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + ret = + gnutls_x509_crl_import (crl_list[0][i - 1], &tmp, GNUTLS_X509_FMT_DER); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + *ncrls = i; + + return 1; /* one certificate parsed */ +} + + +/* Reads a DER or PEM CRL from memory + */ +static int +read_crl_mem (gnutls_certificate_credentials_t res, const void *crl, + int crl_size, gnutls_x509_crt_fmt_t type) +{ + int ret; + + /* allocate space for the certificate to add + */ + res->x509_crl_list = gnutls_realloc_fast (res->x509_crl_list, + (1 + + res->x509_ncrls) * + sizeof (gnutls_x509_crl_t)); + if (res->x509_crl_list == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + if (type == GNUTLS_X509_FMT_DER) + ret = parse_der_crl_mem (&res->x509_crl_list, + &res->x509_ncrls, crl, crl_size); + else + ret = parse_pem_crl_mem (&res->x509_crl_list, + &res->x509_ncrls, crl, crl_size); + + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + return ret; +} + +/** + * gnutls_certificate_set_x509_crl_mem - Used to add CRLs in a gnutls_certificate_credentials_t structure + * @res: is an #gnutls_certificate_credentials_t structure. + * @CRL: is a list of trusted CRLs. They should have been verified before. + * @type: is DER or PEM + * + * This function adds the trusted CRLs in order to verify client or + * server certificates. In case of a client this is not required to + * be called if the certificates are not verified using + * gnutls_certificate_verify_peers2(). This function may be called + * multiple times. + * + * Returns: number of CRLs processed, or a negative value on error. + **/ +int +gnutls_certificate_set_x509_crl_mem (gnutls_certificate_credentials_t + res, const gnutls_datum_t * CRL, + gnutls_x509_crt_fmt_t type) +{ + int ret; + + if ((ret = read_crl_mem (res, CRL->data, CRL->size, type)) < 0) + return ret; + + return ret; +} + +/** + * gnutls_certificate_set_x509_crl - Used to add CRLs in a gnutls_certificate_credentials_t structure + * @res: is an #gnutls_certificate_credentials_t structure. + * @crl_list: is a list of trusted CRLs. They should have been verified before. + * @crl_list_size: holds the size of the crl_list + * + * This function adds the trusted CRLs in order to verify client or + * server certificates. In case of a client this is not required to + * be called if the certificates are not verified using + * gnutls_certificate_verify_peers2(). This function may be called + * multiple times. + * + * Returns: %GNUTLS_E_SUCCESS on success, or an error code. + **/ +int +gnutls_certificate_set_x509_crl (gnutls_certificate_credentials_t res, + gnutls_x509_crl_t * crl_list, + int crl_list_size) +{ + int ret, i; + + res->x509_crl_list = gnutls_realloc_fast (res->x509_crl_list, + (crl_list_size + + res->x509_ncrls) * + sizeof (gnutls_x509_crl_t)); + if (res->x509_crl_list == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + for (i = 0; i < crl_list_size; i++) + { + ret = gnutls_x509_crl_init (&res->x509_crl_list[res->x509_ncrls]); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + ret = _gnutls_x509_crl_cpy (res->x509_crl_list[res->x509_ncrls], + crl_list[i]); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + res->x509_ncrls++; + } + + return 0; +} + +/** + * gnutls_certificate_set_x509_crl_file - Used to add CRLs in a gnutls_certificate_credentials_t structure + * @res: is an #gnutls_certificate_credentials_t structure. + * @crlfile: is a file containing the list of verified CRLs (DER or PEM list) + * @type: is PEM or DER + * + * This function adds the trusted CRLs in order to verify client or server + * certificates. In case of a client this is not required + * to be called if the certificates are not verified using + * gnutls_certificate_verify_peers2(). + * This function may be called multiple times. + * + * Returns: number of CRLs processed or a negative value on error. + **/ +int +gnutls_certificate_set_x509_crl_file (gnutls_certificate_credentials_t + res, const char *crlfile, + gnutls_x509_crt_fmt_t type) +{ + int ret; + size_t size; + char *data = read_binary_file (crlfile, &size); + + if (data == NULL) + { + gnutls_assert (); + return GNUTLS_E_FILE_ERROR; + } + + if (type == GNUTLS_X509_FMT_DER) + ret = parse_der_crl_mem (&res->x509_crl_list, &res->x509_ncrls, + data, size); + else + ret = parse_pem_crl_mem (&res->x509_crl_list, &res->x509_ncrls, + data, size); + + free (data); + + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + return ret; +} + +#include + +static int +parse_pkcs12 (gnutls_certificate_credentials_t res, + gnutls_pkcs12_t p12, + const char *password, + gnutls_x509_privkey_t * key, + gnutls_x509_crt_t * cert, gnutls_x509_crl_t * crl) +{ + gnutls_pkcs12_bag_t bag = NULL; + int index = 0; + int ret; + + for (;;) + { + int elements_in_bag; + int i; + + ret = gnutls_pkcs12_bag_init (&bag); + if (ret < 0) + { + bag = NULL; + gnutls_assert (); + goto done; + } + + ret = gnutls_pkcs12_get_bag (p12, index, bag); + if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) + break; + if (ret < 0) + { + gnutls_assert (); + goto done; + } + + ret = gnutls_pkcs12_bag_get_type (bag, 0); + if (ret < 0) + { + gnutls_assert (); + goto done; + } + + if (ret == GNUTLS_BAG_ENCRYPTED) + { + ret = gnutls_pkcs12_bag_decrypt (bag, password); + if (ret < 0) + { + gnutls_assert (); + goto done; + } + } + + elements_in_bag = gnutls_pkcs12_bag_get_count (bag); + if (elements_in_bag < 0) + { + gnutls_assert (); + goto done; + } + + for (i = 0; i < elements_in_bag; i++) + { + int type; + gnutls_datum_t data; + + type = gnutls_pkcs12_bag_get_type (bag, i); + if (type < 0) + { + gnutls_assert (); + goto done; + } + + ret = gnutls_pkcs12_bag_get_data (bag, i, &data); + if (ret < 0) + { + gnutls_assert (); + goto done; + } + + switch (type) + { + case GNUTLS_BAG_PKCS8_ENCRYPTED_KEY: + case GNUTLS_BAG_PKCS8_KEY: + ret = gnutls_x509_privkey_init (key); + if (ret < 0) + { + gnutls_assert (); + goto done; + } + + ret = gnutls_x509_privkey_import_pkcs8 + (*key, &data, GNUTLS_X509_FMT_DER, password, + type == GNUTLS_BAG_PKCS8_KEY ? GNUTLS_PKCS_PLAIN : 0); + if (ret < 0) + { + gnutls_assert (); + goto done; + } + break; + + case GNUTLS_BAG_CERTIFICATE: + ret = gnutls_x509_crt_init (cert); + if (ret < 0) + { + gnutls_assert (); + goto done; + } + + ret = + gnutls_x509_crt_import (*cert, &data, GNUTLS_X509_FMT_DER); + if (ret < 0) + { + gnutls_assert (); + goto done; + } + break; + + case GNUTLS_BAG_CRL: + ret = gnutls_x509_crl_init (crl); + if (ret < 0) + { + gnutls_assert (); + goto done; + } + + ret = gnutls_x509_crl_import (*crl, &data, GNUTLS_X509_FMT_DER); + if (ret < 0) + { + gnutls_assert (); + goto done; + } + break; + + case GNUTLS_BAG_ENCRYPTED: + /* XXX Bother to recurse one level down? Unlikely to + use the same password anyway. */ + case GNUTLS_BAG_EMPTY: + default: + break; + } + } + + index++; + gnutls_pkcs12_bag_deinit (bag); + } + + ret = 0; + +done: + if (bag) + gnutls_pkcs12_bag_deinit (bag); + + return ret; +} + +/** + * gnutls_certificate_set_x509_simple_pkcs12_file: + * @res: is an #gnutls_certificate_credentials_t structure. + * @pkcs12file: filename of file containing PKCS#12 blob. + * @type: is PEM or DER of the @pkcs12file. + * @password: optional password used to decrypt PKCS#12 file, bags and keys. + * + * This function sets a certificate/private key pair and/or a CRL in + * the gnutls_certificate_credentials_t structure. This function may + * be called more than once (in case multiple keys/certificates exist + * for the server). + * + * MAC:ed PKCS#12 files are supported. Encrypted PKCS#12 bags are + * supported. Encrypted PKCS#8 private keys are supported. However, + * only password based security, and the same password for all + * operations, are supported. + * + * The private keys may be RSA PKCS#1 or DSA private keys encoded in + * the OpenSSL way. + * + * PKCS#12 file may contain many keys and/or certificates, and there + * is no way to identify which key/certificate pair you want. You + * should make sure the PKCS#12 file only contain one key/certificate + * pair and/or one CRL. + * + * It is believed that the limitations of this function is acceptable + * for most usage, and that any more flexibility would introduce + * complexity that would make it harder to use this functionality at + * all. + * + * Returns: %GNUTLS_E_SUCCESS on success, or an error code. + **/ +int + gnutls_certificate_set_x509_simple_pkcs12_file + (gnutls_certificate_credentials_t res, const char *pkcs12file, + gnutls_x509_crt_fmt_t type, const char *password) +{ + gnutls_pkcs12_t p12; + gnutls_datum_t p12blob; + gnutls_x509_privkey_t key = NULL; + gnutls_x509_crt_t cert = NULL; + gnutls_x509_crl_t crl = NULL; + int ret; + size_t size; + + ret = gnutls_pkcs12_init (&p12); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + p12blob.data = read_binary_file (pkcs12file, &size); + p12blob.size = (unsigned int) size; + if (p12blob.data == NULL) + { + gnutls_assert (); + gnutls_pkcs12_deinit (p12); + return GNUTLS_E_FILE_ERROR; + } + + ret = gnutls_pkcs12_import (p12, &p12blob, type, 0); + free (p12blob.data); + if (ret < 0) + { + gnutls_assert (); + gnutls_pkcs12_deinit (p12); + return ret; + } + + if (password) + { + ret = gnutls_pkcs12_verify_mac (p12, password); + if (ret < 0) + { + gnutls_assert (); + gnutls_pkcs12_deinit (p12); + return ret; + } + } + + ret = parse_pkcs12 (res, p12, password, &key, &cert, &crl); + gnutls_pkcs12_deinit (p12); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + if (key && cert) + { + ret = gnutls_certificate_set_x509_key (res, &cert, 1, key); + if (ret < 0) + { + gnutls_assert (); + goto done; + } + } + + if (crl) + { + ret = gnutls_certificate_set_x509_crl (res, &crl, 1); + if (ret < 0) + { + gnutls_assert (); + goto done; + } + } + + ret = 0; + +done: + if (cert) + gnutls_x509_crt_deinit (cert); + if (key) + gnutls_x509_privkey_deinit (key); + if (crl) + gnutls_x509_crl_deinit (crl); + + return ret; +} + + +/** + * gnutls_certificate_free_crls - Used to free all the CRLs from a gnutls_certificate_credentials_t structure + * @sc: is an #gnutls_certificate_credentials_t structure. + * + * This function will delete all the CRLs associated + * with the given credentials. + * + **/ +void +gnutls_certificate_free_crls (gnutls_certificate_credentials_t sc) +{ + unsigned j; + + for (j = 0; j < sc->x509_ncrls; j++) + { + gnutls_x509_crl_deinit (sc->x509_crl_list[j]); + } + + sc->x509_ncrls = 0; + + gnutls_free (sc->x509_crl_list); + sc->x509_crl_list = NULL; +} + +#endif diff --git a/src/daemon/https/tls/gnutls_x509.h b/src/daemon/https/tls/gnutls_x509.h new file mode 100644 index 00000000..3aa0d915 --- /dev/null +++ b/src/daemon/https/tls/gnutls_x509.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +#include + +int _gnutls_x509_cert_verify_peers (gnutls_session_t session, + unsigned int *status); + +#define PEM_CERT_SEP2 "-----BEGIN X509 CERTIFICATE" +#define PEM_CERT_SEP "-----BEGIN CERTIFICATE" +#define PEM_PKCS7_SEP "-----BEGIN PKCS7" + +#define PEM_CRL_SEP "-----BEGIN X509 CRL" + +#define PEM_KEY_RSA_SEP "-----BEGIN RSA" +#define PEM_KEY_DSA_SEP "-----BEGIN DSA" + +int _gnutls_check_key_usage (const gnutls_cert * cert, + gnutls_kx_algorithm_t alg); + +int _gnutls_x509_read_rsa_params (opaque * der, int dersize, mpi_t * params); +int _gnutls_x509_read_dsa_pubkey (opaque * der, int dersize, mpi_t * params); + +int _gnutls_x509_raw_privkey_to_gkey (gnutls_privkey * privkey, + const gnutls_datum_t * raw_key, + gnutls_x509_crt_fmt_t type); +int _gnutls_x509_privkey_to_gkey (gnutls_privkey * privkey, + gnutls_x509_privkey_t); diff --git a/src/daemon/https/tls/gnutlsxx.cpp b/src/daemon/https/tls/gnutlsxx.cpp new file mode 100644 index 00000000..70621a43 --- /dev/null +++ b/src/daemon/https/tls/gnutlsxx.cpp @@ -0,0 +1,907 @@ +#include + +using namespace gnutls; + +inline int RETWRAP_NET(int ret) +{ + if (gnutls_error_is_fatal(ret)) throw(exception(ret)); + else return ret; +} + +inline int RETWRAP(int ret) +{ + if (ret < 0) throw(exception(ret)); + return ret; +} + +session::session( gnutls_connection_end_t end) +{ + RETWRAP(gnutls_init( &this->s, end)); +} + +session::session( session& s) +{ + this->s = s.s; +} + +session::~session() +{ + gnutls_deinit( this->s); +} + +int session::bye( gnutls_close_request_t how) +{ + return RETWRAP_NET( gnutls_bye( this->s, how)); +} + +int session::handshake () +{ + return RETWRAP_NET( gnutls_handshake( this->s)); +} + + +server_session::server_session() : session( GNUTLS_SERVER) +{ +} + +int server_session::rehandshake() +{ + return RETWRAP_NET( gnutls_rehandshake( this->s)); +} + +gnutls_alert_description_t session::get_alert() const +{ + return gnutls_alert_get( this->s); +} + +int session::send_alert ( gnutls_alert_level_t level, + gnutls_alert_description_t desc) +{ + return RETWRAP_NET(gnutls_alert_send( this->s, level, desc)); +} + +int session::send_appropriate_alert (int err) +{ + return RETWRAP_NET(gnutls_alert_send_appropriate( this->s, err)); +} + +gnutls_cipher_algorithm_t session::get_cipher() const +{ + return gnutls_cipher_get( this->s); +} + +gnutls_kx_algorithm_t session::get_kx () const +{ + return gnutls_kx_get( this->s); +} + +gnutls_mac_algorithm_t session::get_mac () const +{ + return gnutls_mac_get( this->s); +} + +gnutls_compression_method_t session::get_compression() const +{ + return gnutls_compression_get( this->s); +} + +gnutls_certificate_type_t session::get_certificate_type() const +{ + return gnutls_certificate_type_get( this->s); +} + +void session::set_private_extensions ( bool allow) +{ + gnutls_handshake_set_private_extensions( this->s, (int)allow); +} + +gnutls_handshake_description_t session::get_handshake_last_out() const +{ + return gnutls_handshake_get_last_out( this->s); +} + +gnutls_handshake_description_t session::get_handshake_last_in() const +{ + return gnutls_handshake_get_last_in( this->s); +} + +ssize_t session::send (const void *data, size_t sizeofdata) +{ + return RETWRAP_NET(gnutls_record_send( this->s, data, sizeofdata)); +} + +ssize_t session::recv (void *data, size_t sizeofdata) +{ + return RETWRAP_NET(gnutls_record_recv( this->s, data, sizeofdata)); +} + +bool session::get_record_direction() const +{ + return gnutls_record_get_direction(this->s); +} + + // maximum packet size +size_t session::get_max_size () const +{ + return gnutls_record_get_max_size( this->s); +} + +void session::set_max_size(size_t size) +{ + RETWRAP( gnutls_record_set_max_size( this->s, size)); +} + +size_t session::check_pending () const +{ + return gnutls_record_check_pending( this->s); +} + + +void session::prf (size_t label_size, const char *label, + int server_random_first, + size_t extra_size, const char *extra, + size_t outsize, char *out) +{ + RETWRAP(gnutls_prf( this->s, label_size, label, server_random_first, + extra_size, extra, outsize, out)); +} + +void session::prf_raw ( size_t label_size, const char *label, + size_t seed_size, const char *seed, + size_t outsize, char *out) +{ + RETWRAP( gnutls_prf_raw( this->s, label_size, label, seed_size, seed, outsize, out)); +} + + +void session::set_cipher_priority (const int *list) +{ + RETWRAP( gnutls_cipher_set_priority( this->s, list)); +} + +void session::set_mac_priority (const int *list) +{ + RETWRAP( gnutls_mac_set_priority( this->s, list)); +} + +void session::set_compression_priority (const int *list) +{ + RETWRAP( gnutls_compression_set_priority( this->s, list)); +} + +void session::set_kx_priority (const int *list) +{ + RETWRAP( gnutls_kx_set_priority( this->s, list)); +} + +void session::set_protocol_priority (const int *list) +{ + RETWRAP( gnutls_protocol_set_priority( this->s, list)); +} + +void session::set_certificate_type_priority (const int *list) +{ + RETWRAP( gnutls_certificate_type_set_priority( this->s, list)); +} + + +/* if you just want some defaults, use the following. + */ +void session::set_priority(const char* prio, const char** err_pos) +{ + RETWRAP(gnutls_priority_set_direct( this->s, prio, err_pos)); +} + +void session::set_priority(gnutls_priority_t p) +{ + RETWRAP(gnutls_priority_set( this->s, p)); +} + +gnutls_protocol_t session::get_protocol_version() const +{ + return gnutls_protocol_get_version( this->s); +} + +void session::set_data ( const void *session_data, + size_t session_data_size) +{ + RETWRAP(gnutls_session_set_data( this->s, session_data, session_data_size)); +} + +void session::get_data (void *session_data, + size_t * session_data_size) const +{ + RETWRAP(gnutls_session_get_data( this->s, session_data, session_data_size)); +} + +void session::get_data(gnutls_session_t session, + gnutls_datum_t & data) const +{ + RETWRAP(gnutls_session_get_data2( this->s, &data)); + +} + +void session::get_id ( void *session_id, + size_t * session_id_size) const +{ + RETWRAP( gnutls_session_get_id( this->s, session_id, session_id_size)); +} + +bool session::is_resumed() const +{ + int ret = gnutls_session_is_resumed( this->s); + + if (ret != 0) return true; + return false; +} + + +bool session::get_peers_certificate(std::vector &out_certs) const +{ + const gnutls_datum_t *certs; + unsigned int certs_size; + + certs = gnutls_certificate_get_peers (this->s, &certs_size); + + if (certs==NULL) return false; + + for(unsigned int i=0;is, certs_size); + + if (*certs==NULL) return false; + return true; +} + +void session::get_our_certificate(gnutls_datum_t& cert) const +{ +const gnutls_datum_t *d; + + d = gnutls_certificate_get_ours(this->s); + if (d==NULL) + throw(exception( GNUTLS_E_INVALID_REQUEST)); + cert = *d; +} + +time_t session::get_peers_certificate_activation_time() const +{ + return gnutls_certificate_activation_time_peers( this->s); +} + +time_t session::get_peers_certificate_expiration_time() const +{ + return gnutls_certificate_expiration_time_peers( this->s); +} +void session::verify_peers_certificate( unsigned int& status) const +{ + RETWRAP( gnutls_certificate_verify_peers2( this->s, &status)); +} + + +client_session::client_session() : session( GNUTLS_CLIENT) +{ +} + +// client session +void client_session::set_server_name (gnutls_server_name_type_t type, + const void *name, size_t name_length) +{ + RETWRAP( gnutls_server_name_set( this->s, type, name, name_length)); +} + +bool client_session::get_request_status() +{ + return RETWRAP(gnutls_certificate_client_get_request_status (this->s)); +} + +// server_session +void server_session::get_server_name (void *data, size_t * data_length, + unsigned int *type, unsigned int indx) const +{ + RETWRAP( gnutls_server_name_get( this->s, data, data_length, type, indx)); +} + +// internal DB stuff +static int store_function(void *_db, gnutls_datum_t key, gnutls_datum_t data) +{ + try { + DB* db = static_cast(_db); + + if (db->store( key, data)==false) return -1; + } catch(...) { + return -1; + } + + return 0; +} + +const static gnutls_datum_t null_datum = { NULL, 0 }; + +static gnutls_datum_t retrieve_function(void *_db, gnutls_datum_t key) +{ + gnutls_datum_t data; + + try { + DB* db = static_cast(_db); + + if (db->retrieve( key, data)==false) return null_datum; + + } catch(...) { + return null_datum; + } + + return data; +} + +static int remove_function(void *_db, gnutls_datum_t key) +{ + try { + DB* db = static_cast(_db); + + if (db->remove( key)==false) return -1; + } catch(...) { + return -1; + } + + return 0; +} + +void server_session::set_db( const DB& db) +{ + gnutls_db_set_ptr( this->s, const_cast(&db)); + gnutls_db_set_store_function( this->s, store_function); + gnutls_db_set_retrieve_function( this->s, retrieve_function); + gnutls_db_set_remove_function( this->s, remove_function); +} + +void server_session::set_db_cache_expiration (unsigned int seconds) +{ + gnutls_db_set_cache_expiration( this->s, seconds); +} + +void server_session::db_remove () const +{ + gnutls_db_remove_session( this->s); +} + +bool server_session::db_check_entry ( gnutls_datum_t &session_data) const +{ + int ret = gnutls_db_check_entry( this->s, session_data); + + if (ret != 0) return true; + return false; +} + +void session::set_max_handshake_packet_length ( size_t max) +{ + gnutls_handshake_set_max_packet_length( this->s, max); +} + +void session::clear_credentials() +{ + gnutls_credentials_clear( this->s); +} + +void session::set_credentials( credentials &cred) +{ + RETWRAP(gnutls_credentials_set( this->s, cred.get_type(), cred.ptr())); +} + +const char* server_session::get_srp_username() const +{ + return gnutls_srp_server_get_username( this->s); +} + +const char* server_session::get_psk_username() const +{ + return gnutls_psk_server_get_username( this->s); +} + + +void session::set_transport_ptr( gnutls_transport_ptr_t ptr) +{ + gnutls_transport_set_ptr( this->s, ptr); +} + +void session::set_transport_ptr( gnutls_transport_ptr_t recv_ptr, gnutls_transport_ptr_t send_ptr) +{ + gnutls_transport_set_ptr2( this->s, recv_ptr, send_ptr); +} + + +gnutls_transport_ptr_t session::get_transport_ptr () const +{ + return gnutls_transport_get_ptr (this->s); +} + +void session::get_transport_ptr( gnutls_transport_ptr_t & recv_ptr, + gnutls_transport_ptr_t & send_ptr) const +{ + gnutls_transport_get_ptr2 (this->s, &recv_ptr, &send_ptr); +} + +void session::set_transport_lowat( size_t num) +{ + gnutls_transport_set_lowat (this->s, num); +} + +void session::set_transport_push_function( gnutls_push_func push_func) +{ + gnutls_transport_set_push_function ( this->s, push_func); +} + +void session::set_transport_pull_function( gnutls_pull_func pull_func) +{ + gnutls_transport_set_pull_function ( this->s, pull_func); +} + +void session::set_user_ptr( void* ptr) +{ + gnutls_session_set_ptr( this->s, ptr); +} + +void* session::get_user_ptr( ) const +{ + return gnutls_session_get_ptr(this->s); +} + +void session::send_openpgp_cert( gnutls_openpgp_crt_status_t status) +{ + gnutls_openpgp_send_cert(this->s, status); +} + + +void session::set_dh_prime_bits( unsigned int bits) +{ + gnutls_dh_set_prime_bits( this->s, bits); +} + +unsigned int session::get_dh_secret_bits() const +{ + return RETWRAP( gnutls_dh_get_secret_bits( this->s)); +} + +unsigned int session::get_dh_peers_public_bits() const +{ + return RETWRAP(gnutls_dh_get_peers_public_bits( this->s)); +} + +unsigned int session::get_dh_prime_bits() const +{ + return RETWRAP( gnutls_dh_get_prime_bits( this->s)); +} + +void session::get_dh_group( gnutls_datum_t & gen, gnutls_datum_t & prime) const +{ + RETWRAP( gnutls_dh_get_group( this->s, &gen, &prime)); +} + +void session::get_dh_pubkey( gnutls_datum_t & raw_key) const +{ + RETWRAP(gnutls_dh_get_pubkey( this->s, &raw_key)); +} + +void session::get_rsa_export_pubkey( gnutls_datum_t& exponent, gnutls_datum_t& modulus) const +{ + RETWRAP( gnutls_rsa_export_get_pubkey( this->s, &exponent, &modulus)); +} + +unsigned int session::get_rsa_export_modulus_bits() const +{ + return RETWRAP(gnutls_rsa_export_get_modulus_bits( this->s)); +} + +void server_session::set_certificate_request( gnutls_certificate_request_t req) +{ + gnutls_certificate_server_set_request (this->s, req); +} + + + + +gnutls_credentials_type_t session::get_auth_type() const +{ + return gnutls_auth_get_type( this->s); +} + +gnutls_credentials_type_t session::get_server_auth_type() const +{ + return gnutls_auth_server_get_type( this->s); +} + +gnutls_credentials_type_t session::get_client_auth_type() const +{ + return gnutls_auth_client_get_type( this->s); +} + + +void* certificate_credentials::ptr() const +{ + return this->cred; +} + +void certificate_credentials::set_ptr(void* p) +{ + this->cred = static_cast (p); +} + +certificate_credentials::~certificate_credentials() +{ + gnutls_certificate_free_credentials (this->cred); +} + +certificate_credentials::certificate_credentials() : credentials(GNUTLS_CRD_CERTIFICATE) +{ + RETWRAP(gnutls_certificate_allocate_credentials ( &this->cred)); +} + +void certificate_server_credentials::set_params_function( gnutls_params_function* func) +{ + gnutls_certificate_set_params_function( this->cred, func); +} + +anon_server_credentials::anon_server_credentials() : credentials(GNUTLS_CRD_ANON) +{ + RETWRAP(gnutls_anon_allocate_server_credentials( &this->cred)); +} + +anon_server_credentials::~anon_server_credentials() +{ + gnutls_anon_free_server_credentials( this->cred); +} + +void anon_server_credentials::set_dh_params( const dh_params& params) +{ + gnutls_anon_set_server_dh_params (this->cred, params.get_params_t()); +} + +void anon_server_credentials::set_params_function ( gnutls_params_function * func) +{ + gnutls_anon_set_server_params_function ( this->cred, func); +} + +anon_client_credentials::anon_client_credentials() : credentials(GNUTLS_CRD_ANON) +{ + RETWRAP(gnutls_anon_allocate_client_credentials( &this->cred)); +} + +anon_client_credentials::~anon_client_credentials() +{ + gnutls_anon_free_client_credentials( this->cred); +} + +void certificate_credentials::free_keys () +{ + gnutls_certificate_free_keys( this->cred); +} + +void certificate_credentials::free_cas () +{ + gnutls_certificate_free_cas( this->cred); +} + +void certificate_credentials::free_ca_names () +{ + gnutls_certificate_free_ca_names( this->cred); +} + +void certificate_credentials::free_crls () +{ + gnutls_certificate_free_crls( this->cred); +} + + +void certificate_credentials::set_dh_params ( const dh_params& params) +{ + gnutls_certificate_set_dh_params( this->cred, params.get_params_t()); +} + +void certificate_credentials::set_rsa_export_params ( const rsa_params & params) +{ + gnutls_certificate_set_rsa_export_params( this->cred, params.get_params_t()); +} + +void certificate_credentials::set_verify_flags ( unsigned int flags) +{ + gnutls_certificate_set_verify_flags( this->cred, flags); +} + +void certificate_credentials::set_verify_limits ( unsigned int max_bits, unsigned int max_depth) +{ + gnutls_certificate_set_verify_limits( this->cred, max_bits, max_depth); +} + +void certificate_credentials::set_x509_trust_file(const char *cafile, gnutls_x509_crt_fmt_t type) +{ + RETWRAP( gnutls_certificate_set_x509_trust_file( this->cred, cafile, type)); +} + +void certificate_credentials::set_x509_trust(const gnutls_datum_t & CA, gnutls_x509_crt_fmt_t type) +{ + RETWRAP( gnutls_certificate_set_x509_trust_mem( this->cred, &CA, type)); +} + + +void certificate_credentials::set_x509_crl_file( const char *crlfile, gnutls_x509_crt_fmt_t type) +{ + RETWRAP( gnutls_certificate_set_x509_crl_file( this->cred, crlfile, type)); +} + +void certificate_credentials::set_x509_crl(const gnutls_datum_t & CRL, gnutls_x509_crt_fmt_t type) +{ + RETWRAP( gnutls_certificate_set_x509_crl_mem( this->cred, &CRL, type)); +} + +void certificate_credentials::set_x509_key_file(const char *certfile, const char *keyfile, gnutls_x509_crt_fmt_t type) +{ + RETWRAP( gnutls_certificate_set_x509_key_file( this->cred, certfile, keyfile, type)); +} + +void certificate_credentials::set_x509_key(const gnutls_datum_t & CERT, const gnutls_datum_t & KEY, gnutls_x509_crt_fmt_t type) +{ + RETWRAP( gnutls_certificate_set_x509_key_mem( this->cred, &CERT, &KEY, type)); +} + +void certificate_credentials::set_simple_pkcs12_file( const char *pkcs12file, + gnutls_x509_crt_fmt_t type, const char *password) +{ + RETWRAP( gnutls_certificate_set_x509_simple_pkcs12_file( this->cred, pkcs12file, type, password)); +} + +void certificate_credentials::set_x509_key ( gnutls_x509_crt_t * cert_list, int cert_list_size, + gnutls_x509_privkey_t key) +{ + RETWRAP( gnutls_certificate_set_x509_key( this->cred, cert_list, cert_list_size, key)); +} + +void certificate_credentials::set_x509_trust ( gnutls_x509_crt_t * ca_list, int ca_list_size) +{ + RETWRAP( gnutls_certificate_set_x509_trust( this->cred, ca_list, ca_list_size)); +} + +void certificate_credentials::set_x509_crl ( gnutls_x509_crl_t * crl_list, int crl_list_size) +{ + RETWRAP( gnutls_certificate_set_x509_crl( this->cred, crl_list, crl_list_size)); +} + +void certificate_server_credentials::set_retrieve_function( gnutls_certificate_server_retrieve_function* func) +{ + gnutls_certificate_server_set_retrieve_function( this->cred, func); +} + +void certificate_client_credentials::set_retrieve_function( gnutls_certificate_client_retrieve_function* func) +{ + gnutls_certificate_client_set_retrieve_function( this->cred, func); +} + +// SRP + +srp_server_credentials::srp_server_credentials() : credentials(GNUTLS_CRD_SRP) +{ + RETWRAP(gnutls_srp_allocate_server_credentials( &this->cred)); +} + +srp_server_credentials::~srp_server_credentials() +{ + gnutls_srp_free_server_credentials( this->cred); +} + +void* srp_server_credentials::ptr() const +{ + return this->cred; +} + +void srp_server_credentials::set_ptr(void* p) +{ + this->cred = static_cast (p); +} + +srp_client_credentials::srp_client_credentials() : credentials(GNUTLS_CRD_SRP) +{ + RETWRAP(gnutls_srp_allocate_client_credentials( &this->cred)); +} + +srp_client_credentials::~srp_client_credentials() +{ + gnutls_srp_free_client_credentials( this->cred); +} + +void* srp_client_credentials::ptr() const +{ + return this->cred; +} + +void srp_client_credentials::set_ptr(void* p) +{ + this->cred = static_cast (p); +} + +void srp_client_credentials::set_credentials( const char* username, const char* password) +{ + RETWRAP(gnutls_srp_set_client_credentials (this->cred, username, password)); +} + +void srp_server_credentials::set_credentials_file ( + const char *password_file, const char *password_conf_file) +{ + RETWRAP( gnutls_srp_set_server_credentials_file( this->cred, password_file, password_conf_file)); +} + + +void srp_server_credentials::set_credentials_function(gnutls_srp_server_credentials_function * func) +{ + gnutls_srp_set_server_credentials_function( this->cred, func); +} + +void srp_client_credentials::set_credentials_function(gnutls_srp_client_credentials_function * func) +{ + gnutls_srp_set_client_credentials_function( this->cred, func); +} + +credentials::credentials(gnutls_credentials_type_t t) : type(t) +{ +} + +#if !(defined(__APPLE__) || defined(__MACOS__)) +/* FIXME: This #if is due to a compile bug in Mac OS X. Give it some + time and then remove this cruft. See also + includes/gnutls/gnutlsxx.h. */ +credentials::credentials( credentials& c) +{ + this->type = c.type; + this->set_ptr( c.ptr()); +} +#endif + +gnutls_credentials_type_t credentials::get_type() const +{ + return type; +} + +exception::exception( int x) +{ + retcode = x; +} + +int exception::get_code() +{ + return retcode; +} + +const char* exception::what() const throw() +{ + return gnutls_strerror(retcode); +} + + + + +dh_params::dh_params() +{ + RETWRAP(gnutls_dh_params_init( ¶ms)); +} + +dh_params::~dh_params() +{ + gnutls_dh_params_deinit(params); +} + +void dh_params::import_raw( const gnutls_datum_t & prime, + const gnutls_datum_t & generator) +{ + RETWRAP( gnutls_dh_params_import_raw( params, &prime, &generator)); +} + +void dh_params::import_pkcs3( const gnutls_datum_t & pkcs3_params, + gnutls_x509_crt_fmt_t format) +{ + RETWRAP(gnutls_dh_params_import_pkcs3( params, &pkcs3_params, format)); +} + +void dh_params::generate( unsigned int bits) +{ + RETWRAP(gnutls_dh_params_generate2( params, bits)); +} + +void dh_params::export_pkcs3( gnutls_x509_crt_fmt_t format, unsigned char *params_data, size_t * params_data_size) +{ + RETWRAP( gnutls_dh_params_export_pkcs3( params, format, params_data, params_data_size)); +} + +void dh_params::export_raw( gnutls_datum_t& prime, gnutls_datum_t &generator) +{ + RETWRAP( gnutls_dh_params_export_raw( params, &prime, &generator, NULL)); +} + +gnutls_dh_params_t dh_params::get_params_t() const +{ + return params; +} + +dh_params & dh_params::operator=(const dh_params& src) +{ + dh_params* dst = new dh_params; + int ret; + + ret = gnutls_dh_params_cpy( dst->params, src.params); + + if (ret < 0) { + delete dst; + throw(ret); + } + + return *dst; +} + + +// RSA + +rsa_params::rsa_params() +{ + RETWRAP(gnutls_rsa_params_init( ¶ms)); +} + +rsa_params::~rsa_params() +{ + gnutls_rsa_params_deinit(params); +} + +void rsa_params::import_pkcs1( const gnutls_datum_t & pkcs1_params, + gnutls_x509_crt_fmt_t format) +{ + RETWRAP(gnutls_rsa_params_import_pkcs1( params, &pkcs1_params, format)); +} + +void rsa_params::generate( unsigned int bits) +{ + RETWRAP(gnutls_rsa_params_generate2( params, bits)); +} + +void rsa_params::export_pkcs1( gnutls_x509_crt_fmt_t format, unsigned char *params_data, size_t * params_data_size) +{ + RETWRAP( gnutls_rsa_params_export_pkcs1( params, format, params_data, params_data_size)); +} + +gnutls_rsa_params_t rsa_params::get_params_t() const +{ + return params; +} + +rsa_params & rsa_params::operator=(const rsa_params& src) +{ + rsa_params* dst = new rsa_params; + int ret; + + ret = gnutls_rsa_params_cpy( dst->params, src.params); + + if (ret < 0) + delete dst; + throw(ret); + + return *dst; +} + +void rsa_params::import_raw( const gnutls_datum_t & m, + const gnutls_datum_t & e, + const gnutls_datum_t & d, + const gnutls_datum_t & p, + const gnutls_datum_t & q, + const gnutls_datum_t & u) +{ + + RETWRAP(gnutls_rsa_params_import_raw ( params, &m, &e, &d, &p, &q, &u)); +} + + +void rsa_params::export_raw( gnutls_datum_t & m, gnutls_datum_t & e, + gnutls_datum_t & d, gnutls_datum_t & p, + gnutls_datum_t & q, gnutls_datum_t & u) +{ + RETWRAP( gnutls_rsa_params_export_raw ( params, &m, &e, &d, &p, &q, &u, NULL)); +} diff --git a/src/daemon/https/tls/io_debug.h b/src/daemon/https/tls/io_debug.h new file mode 100644 index 00000000..9800565e --- /dev/null +++ b/src/daemon/https/tls/io_debug.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +/* This debug file was contributed by + * Paul Sheer . Some changes were made by nmav. + * Its purpose is to debug non blocking behaviour of gnutls. The included + * send() and recv() functions return EAGAIN errors in random. + * + */ + +#ifdef IO_DEBUG + +#include + +#define EDUNNO EAGAIN /* EAGAIN */ + +extern int errno; +static int initialized_rand = 0; + +#define INITIALIZE_RAND if (initialized_rand==0) {\ + srand(time(0)); \ + initialized_rand = 1; \ + } +static int +recv_debug (int fd, char *buf, int len, int flags) +{ + INITIALIZE_RAND; + + if (!(rand () % IO_DEBUG)) + { + errno = EDUNNO; + return -1; + } + if (len > 1) + len = 1; + return recv (fd, buf, len, flags); +} + +#define recv recv_debug + +static int +send_debug (int fd, const char *buf, int len, int flags) +{ + INITIALIZE_RAND; + + if (!(rand () % IO_DEBUG)) + { + errno = EDUNNO; + return -1; + } + if (len > 10) + len = 10; + return send (fd, buf, len, flags); +} + +#define send send_debug + +#endif diff --git a/src/daemon/https/tls/libgnutls-config b/src/daemon/https/tls/libgnutls-config new file mode 100644 index 00000000..80580a89 --- /dev/null +++ b/src/daemon/https/tls/libgnutls-config @@ -0,0 +1,104 @@ +#!/bin/sh + +prefix=/home/lama/workbench/programming/c/gnunet/gnutls-2.2.3/build +exec_prefix=${prefix} +exec_prefix_set=no + +gnutls_libs="-L${exec_prefix}/lib -lgnutls -L/usr/lib -ltasn1 -lgcrypt " +gnutls_cflags=" -I/usr/include -I${prefix}/include" +gnutls_la_file="${exec_prefix}/lib/libgnutls.la" + +usage() +{ + cat <&2 +fi + +while test $# -gt 0; do + case "$1" in + -*=*) optarg=`echo "$1" | sed 's/[-_a-zA-Z0-9]*=//'` ;; + *) optarg= ;; + esac + + case $1 in + --prefix=*) + prefix=$optarg + if test $exec_prefix_set = no ; then + exec_prefix=$optarg + fi + ;; + --prefix) + echo_prefix=yes + ;; + --exec-prefix=*) + exec_prefix=$optarg + exec_prefix_set=yes + ;; + --exec-prefix) + echo_exec_prefix=yes + ;; + --version) + echo "2.2.3" + exit 0 + ;; + --cflags) + echo_cflags=yes + ;; + --libs) + echo_libs=yes + ;; + --la-file) + echo_la_file=yes + ;; + --help) + usage 0 + ;; + *) + usage 1 1>&2 + ;; + esac + shift +done + +if test "$echo_prefix" = "yes"; then + echo $prefix +fi + +if test "$echo_exec_prefix" = "yes"; then + echo $exec_prefix +fi + +if test "$echo_cflags" = "yes"; then + if test "${prefix}/include" != "/usr/include" ; then + includes="-I${prefix}/include" + for i in $gnutls_cflags ; do + if test "$i" = "-I${prefix}/include" ; then + includes="" + fi + done + fi + echo $includes $gnutls_cflags +fi + +if test "$echo_la_file" = "yes"; then + echo ${gnutls_la_file} +fi + +if test "$echo_libs" = "yes"; then + echo ${gnutls_libs} +fi + + + diff --git a/src/daemon/https/tls/libgnutls.m4 b/src/daemon/https/tls/libgnutls.m4 new file mode 100644 index 00000000..1851ca23 --- /dev/null +++ b/src/daemon/https/tls/libgnutls.m4 @@ -0,0 +1,160 @@ +dnl Autoconf macros for libgnutls +dnl $id$ + +# Modified for LIBGNUTLS -- nmav +# Configure paths for LIBGCRYPT +# Shamelessly stolen from the one of XDELTA by Owen Taylor +# Werner Koch 99-12-09 + +dnl AM_PATH_LIBGNUTLS([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND ]]]) +dnl Test for libgnutls, and define LIBGNUTLS_CFLAGS and LIBGNUTLS_LIBS +dnl +AC_DEFUN([AM_PATH_LIBGNUTLS], +[dnl +dnl Get the cflags and libraries from the libgnutls-config script +dnl +AC_ARG_WITH(libgnutls-prefix, + [ --with-libgnutls-prefix=PFX Prefix where libgnutls is installed (optional)], + libgnutls_config_prefix="$withval", libgnutls_config_prefix="") + + if test x$libgnutls_config_prefix != x ; then + if test x${LIBGNUTLS_CONFIG+set} != xset ; then + LIBGNUTLS_CONFIG=$libgnutls_config_prefix/bin/libgnutls-config + fi + fi + + AC_PATH_PROG(LIBGNUTLS_CONFIG, libgnutls-config, no) + min_libgnutls_version=ifelse([$1], ,0.1.0,$1) + AC_MSG_CHECKING(for libgnutls - version >= $min_libgnutls_version) + no_libgnutls="" + if test "$LIBGNUTLS_CONFIG" = "no" ; then + no_libgnutls=yes + else + LIBGNUTLS_CFLAGS=`$LIBGNUTLS_CONFIG $libgnutls_config_args --cflags` + LIBGNUTLS_LIBS=`$LIBGNUTLS_CONFIG $libgnutls_config_args --libs` + libgnutls_config_version=`$LIBGNUTLS_CONFIG $libgnutls_config_args --version` + + + ac_save_CFLAGS="$CFLAGS" + ac_save_LIBS="$LIBS" + CFLAGS="$CFLAGS $LIBGNUTLS_CFLAGS" + LIBS="$LIBS $LIBGNUTLS_LIBS" +dnl +dnl Now check if the installed libgnutls is sufficiently new. Also sanity +dnl checks the results of libgnutls-config to some extent +dnl + rm -f conf.libgnutlstest + AC_TRY_RUN([ +#include +#include +#include +#include + +int +main () +{ + system ("touch conf.libgnutlstest"); + + if( strcmp( gnutls_check_version(NULL), "$libgnutls_config_version" ) ) + { + printf("\n*** 'libgnutls-config --version' returned %s, but LIBGNUTLS (%s)\n", + "$libgnutls_config_version", gnutls_check_version(NULL) ); + printf("*** was found! If libgnutls-config was correct, then it is best\n"); + printf("*** to remove the old version of LIBGNUTLS. You may also be able to fix the error\n"); + printf("*** by modifying your LD_LIBRARY_PATH enviroment variable, or by editing\n"); + printf("*** /etc/ld.so.conf. Make sure you have run ldconfig if that is\n"); + printf("*** required on your system.\n"); + printf("*** If libgnutls-config was wrong, set the environment variable LIBGNUTLS_CONFIG\n"); + printf("*** to point to the correct copy of libgnutls-config, and remove the file config.cache\n"); + printf("*** before re-running configure\n"); + } + else if ( strcmp(gnutls_check_version(NULL), LIBGNUTLS_VERSION ) ) + { + printf("\n*** LIBGNUTLS header file (version %s) does not match\n", LIBGNUTLS_VERSION); + printf("*** library (version %s)\n", gnutls_check_version(NULL) ); + } + else + { + if ( gnutls_check_version( "$min_libgnutls_version" ) ) + { + return 0; + } + else + { + printf("no\n*** An old version of LIBGNUTLS (%s) was found.\n", + gnutls_check_version(NULL) ); + printf("*** You need a version of LIBGNUTLS newer than %s. The latest version of\n", + "$min_libgnutls_version" ); + printf("*** LIBGNUTLS is always available from ftp://gnutls.hellug.gr/pub/gnutls.\n"); + printf("*** \n"); + printf("*** If you have already installed a sufficiently new version, this error\n"); + printf("*** probably means that the wrong copy of the libgnutls-config shell script is\n"); + printf("*** being found. The easiest way to fix this is to remove the old version\n"); + printf("*** of LIBGNUTLS, but you can also set the LIBGNUTLS_CONFIG environment to point to the\n"); + printf("*** correct copy of libgnutls-config. (In this case, you will have to\n"); + printf("*** modify your LD_LIBRARY_PATH enviroment variable, or edit /etc/ld.so.conf\n"); + printf("*** so that the correct libraries are found at run-time))\n"); + } + } + return 1; +} +],, no_libgnutls=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"]) + CFLAGS="$ac_save_CFLAGS" + LIBS="$ac_save_LIBS" + fi + + if test "x$no_libgnutls" = x ; then + AC_MSG_RESULT(yes) + ifelse([$2], , :, [$2]) + else + if test -f conf.libgnutlstest ; then + : + else + AC_MSG_RESULT(no) + fi + if test "$LIBGNUTLS_CONFIG" = "no" ; then + echo "*** The libgnutls-config script installed by LIBGNUTLS could not be found" + echo "*** If LIBGNUTLS was installed in PREFIX, make sure PREFIX/bin is in" + echo "*** your path, or set the LIBGNUTLS_CONFIG environment variable to the" + echo "*** full path to libgnutls-config." + else + if test -f conf.libgnutlstest ; then + : + else + echo "*** Could not run libgnutls test program, checking why..." + CFLAGS="$CFLAGS $LIBGNUTLS_CFLAGS" + LIBS="$LIBS $LIBGNUTLS_LIBS" + AC_TRY_LINK([ +#include +#include +#include +#include +], [ return !!gnutls_check_version(NULL); ], + [ echo "*** The test program compiled, but did not run. This usually means" + echo "*** that the run-time linker is not finding LIBGNUTLS or finding the wrong" + echo "*** version of LIBGNUTLS. If it is not finding LIBGNUTLS, you'll need to set your" + echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point" + echo "*** to the installed location Also, make sure you have run ldconfig if that" + echo "*** is required on your system" + echo "***" + echo "*** If you have an old version installed, it is best to remove it, although" + echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH" + echo "***" ], + [ echo "*** The test program failed to compile or link. See the file config.log for the" + echo "*** exact error that occured. This usually means LIBGNUTLS was incorrectly installed" + echo "*** or that you have moved LIBGNUTLS since it was installed. In the latter case, you" + echo "*** may want to edit the libgnutls-config script: $LIBGNUTLS_CONFIG" ]) + CFLAGS="$ac_save_CFLAGS" + LIBS="$ac_save_LIBS" + fi + fi + LIBGNUTLS_CFLAGS="" + LIBGNUTLS_LIBS="" + ifelse([$3], , :, [$3]) + fi + rm -f conf.libgnutlstest + AC_SUBST(LIBGNUTLS_CFLAGS) + AC_SUBST(LIBGNUTLS_LIBS) +]) + +dnl *-*wedit:notab*-* Please keep this as the last line. diff --git a/src/daemon/https/tls/libgnutls.vers b/src/daemon/https/tls/libgnutls.vers new file mode 100644 index 00000000..f793617b --- /dev/null +++ b/src/daemon/https/tls/libgnutls.vers @@ -0,0 +1,27 @@ +# libgnutls.vers -- Versioning script to control what symbols to export. +# Copyright (C) 2005, 2006, 2007 Free Software Foundation +# +# Author: Simon Josefsson +# +# This file is part of GNUTLS. +# +# The GNUTLS library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 of +# the License, or (at your option) any later version. +# +# The GNUTLS library 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 +#Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with the GNUTLS library; if not, write to the Free +# Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA + +GNUTLS_1_4 +{ + global: _gnutls*; gnutls*; + local: *; +}; diff --git a/src/daemon/https/tls/libgnutlsxx.vers b/src/daemon/https/tls/libgnutlsxx.vers new file mode 100644 index 00000000..8b8af51d --- /dev/null +++ b/src/daemon/https/tls/libgnutlsxx.vers @@ -0,0 +1,30 @@ +# libgnutlsxx.vers -- Versioning script to control what symbols to export. +# Copyright (C) 2005, 2006 Free Software Foundation +# +# Author: Simon Josefsson +# +# This file is part of GNUTLS. +# +# The GNUTLS library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 of +# the License, or (at your option) any later version. +# +# The GNUTLS library 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 +#Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with the GNUTLS library; if not, write to the Free +# Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA + +GNUTLS_1_6 +{ + global: + extern "C++" { + gnutls*; + }; + local: *; +}; diff --git a/src/daemon/https/tls/pkix.asn b/src/daemon/https/tls/pkix.asn new file mode 100644 index 00000000..d46dfa07 --- /dev/null +++ b/src/daemon/https/tls/pkix.asn @@ -0,0 +1,1241 @@ + +PKIX1 { } + +DEFINITIONS IMPLICIT TAGS ::= + +BEGIN + +-- This contains both PKIX1Implicit88 and RFC2630 ASN.1 modules. + +-- ISO arc for standard certificate and CRL extensions + +id-ce OBJECT IDENTIFIER ::= {joint-iso-ccitt(2) ds(5) 29} + + +-- authority key identifier OID and syntax + +id-ce-authorityKeyIdentifier OBJECT IDENTIFIER ::= { id-ce 35 } + +AuthorityKeyIdentifier ::= SEQUENCE { + keyIdentifier [0] KeyIdentifier OPTIONAL, + authorityCertIssuer [1] GeneralNames OPTIONAL, + authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL } + -- authorityCertIssuer and authorityCertSerialNumber shall both + -- be present or both be absgent + +KeyIdentifier ::= OCTET STRING + +-- subject key identifier OID and syntax + +id-ce-subjectKeyIdentifier OBJECT IDENTIFIER ::= { id-ce 14 } + +SubjectKeyIdentifier ::= KeyIdentifier + +-- key usage extension OID and syntax + +id-ce-keyUsage OBJECT IDENTIFIER ::= { id-ce 15 } + +KeyUsage ::= BIT STRING { + digitalSignature (0), + nonRepudiation (1), + keyEncipherment (2), + dataEncipherment (3), + keyAgreement (4), + keyCertSign (5), + cRLSign (6), + encipherOnly (7), + decipherOnly (8) } + +-- private key usage period extension OID and syntax + +id-ce-privateKeyUsagePeriod OBJECT IDENTIFIER ::= { id-ce 16 } + +PrivateKeyUsagePeriod ::= SEQUENCE { + notBefore [0] GeneralizedTime OPTIONAL, + notAfter [1] GeneralizedTime OPTIONAL } + -- either notBefore or notAfter shall be present + +-- certificate policies extension OID and syntax + +id-ce-certificatePolicies OBJECT IDENTIFIER ::= { id-ce 32 } + +CertificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation + +PolicyInformation ::= SEQUENCE { + policyIdentifier CertPolicyId, + policyQualifiers SEQUENCE SIZE (1..MAX) OF + PolicyQualifierInfo OPTIONAL } + +CertPolicyId ::= OBJECT IDENTIFIER + +PolicyQualifierInfo ::= SEQUENCE { + policyQualifierId PolicyQualifierId, + qualifier ANY DEFINED BY policyQualifierId } + +-- Implementations that recognize additional policy qualifiers shall +-- augment the following definition for PolicyQualifierId + +PolicyQualifierId ::= + OBJECT IDENTIFIER -- ( id-qt-cps | id-qt-unotice ) + +-- CPS pointer qualifier + +CPSuri ::= IA5String + +-- user notice qualifier + +UserNotice ::= SEQUENCE { + noticeRef NoticeReference OPTIONAL, + explicitText DisplayText OPTIONAL} + +NoticeReference ::= SEQUENCE { + organization DisplayText, + noticeNumbers SEQUENCE OF INTEGER } + +DisplayText ::= CHOICE { + visibleString VisibleString (SIZE (1..200)), + bmpString BMPString (SIZE (1..200)), + utf8String UTF8String (SIZE (1..200)) } + +-- policy mapping extension OID and syntax + +id-ce-policyMappings OBJECT IDENTIFIER ::= { id-ce 33 } + +PolicyMappings ::= SEQUENCE SIZE (1..MAX) OF SEQUENCE { + issuerDomainPolicy CertPolicyId, + subjectDomainPolicy CertPolicyId } + +-- subject alternative name extension OID and syntax + +-- Directory string type -- + +DirectoryString ::= CHOICE { + teletexString TeletexString (SIZE (1..MAX)), + printableString PrintableString (SIZE (1..MAX)), + universalString UniversalString (SIZE (1..MAX)), + utf8String UTF8String (SIZE (1..MAX)), + bmpString BMPString (SIZE(1..MAX)), + -- IA5String is added here to handle old UID encoded as ia5String -- + -- See tests/userid/ for more information. It shouldn't be here, -- + -- so if it causes problems, considering dropping it. -- + ia5String IA5String (SIZE(1..MAX)) } + +id-ce-subjectAltName OBJECT IDENTIFIER ::= { id-ce 17 } + +SubjectAltName ::= GeneralNames + +GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName + +GeneralName ::= CHOICE { + otherName [0] AnotherName, + rfc822Name [1] IA5String, + dNSName [2] IA5String, + x400Address [3] ORAddress, +-- Changed to work with the libtasn1 parser. + directoryName [4] EXPLICIT RDNSequence, --Name, + ediPartyName [5] EDIPartyName, + uniformResourceIdentifier [6] IA5String, + iPAddress [7] OCTET STRING, + registeredID [8] OBJECT IDENTIFIER } + +-- AnotherName replaces OTHER-NAME ::= TYPE-IDENTIFIER, as +-- TYPE-IDENTIFIER is not supported in the '88 ASN.1 syntax + +AnotherName ::= SEQUENCE { + type-id OBJECT IDENTIFIER, + value [0] EXPLICIT ANY DEFINED BY type-id } + +EDIPartyName ::= SEQUENCE { + nameAssigner [0] DirectoryString OPTIONAL, + partyName [1] DirectoryString } + +-- issuer alternative name extension OID and syntax + +id-ce-issuerAltName OBJECT IDENTIFIER ::= { id-ce 18 } + +IssuerAltName ::= GeneralNames + +id-ce-subjectDirectoryAttributes OBJECT IDENTIFIER ::= { id-ce 9 } + +SubjectDirectoryAttributes ::= SEQUENCE SIZE (1..MAX) OF Attribute + +-- basic constraints extension OID and syntax + +id-ce-basicConstraints OBJECT IDENTIFIER ::= { id-ce 19 } + +BasicConstraints ::= SEQUENCE { + cA BOOLEAN DEFAULT FALSE, + pathLenConstraint INTEGER (0..MAX) OPTIONAL } + +-- name constraints extension OID and syntax + +id-ce-nameConstraints OBJECT IDENTIFIER ::= { id-ce 30 } + +NameConstraints ::= SEQUENCE { + permittedSubtrees [0] GeneralSubtrees OPTIONAL, + excludedSubtrees [1] GeneralSubtrees OPTIONAL } + +GeneralSubtrees ::= SEQUENCE SIZE (1..MAX) OF GeneralSubtree + +GeneralSubtree ::= SEQUENCE { + base GeneralName, + minimum [0] BaseDistance DEFAULT 0, + maximum [1] BaseDistance OPTIONAL } + +BaseDistance ::= INTEGER (0..MAX) + +-- policy constraints extension OID and syntax + +id-ce-policyConstraints OBJECT IDENTIFIER ::= { id-ce 36 } + +PolicyConstraints ::= SEQUENCE { + requireExplicitPolicy [0] SkipCerts OPTIONAL, + inhibitPolicyMapping [1] SkipCerts OPTIONAL } + +SkipCerts ::= INTEGER (0..MAX) + +-- CRL distribution points extension OID and syntax + +id-ce-cRLDistributionPoints OBJECT IDENTIFIER ::= {id-ce 31} + +CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint + +DistributionPoint ::= SEQUENCE { + distributionPoint [0] EXPLICIT DistributionPointName OPTIONAL, + reasons [1] ReasonFlags OPTIONAL, + cRLIssuer [2] GeneralNames OPTIONAL +} + +DistributionPointName ::= CHOICE { + fullName [0] GeneralNames, + nameRelativeToCRLIssuer [1] RelativeDistinguishedName +} + +ReasonFlags ::= BIT STRING { + unused (0), + keyCompromise (1), + cACompromise (2), + affiliationChanged (3), + superseded (4), + cessationOfOperation (5), + certificateHold (6), + privilegeWithdrawn (7), + aACompromise (8) } + +-- extended key usage extension OID and syntax + +id-ce-extKeyUsage OBJECT IDENTIFIER ::= {id-ce 37} + +ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId + +KeyPurposeId ::= OBJECT IDENTIFIER + +-- extended key purpose OIDs +id-kp-serverAuth OBJECT IDENTIFIER ::= { id-kp 1 } +id-kp-clientAuth OBJECT IDENTIFIER ::= { id-kp 2 } +id-kp-codeSigning OBJECT IDENTIFIER ::= { id-kp 3 } +id-kp-emailProtection OBJECT IDENTIFIER ::= { id-kp 4 } +id-kp-ipsecEndSystem OBJECT IDENTIFIER ::= { id-kp 5 } +id-kp-ipsecTunnel OBJECT IDENTIFIER ::= { id-kp 6 } +id-kp-ipsecUser OBJECT IDENTIFIER ::= { id-kp 7 } +id-kp-timeStamping OBJECT IDENTIFIER ::= { id-kp 8 } + +-- authority info access + +id-pe-authorityInfoAccess OBJECT IDENTIFIER ::= { id-pe 1 } + +AuthorityInfoAccessSyntax ::= + SEQUENCE SIZE (1..MAX) OF AccessDescription + +AccessDescription ::= SEQUENCE { + accessMethod OBJECT IDENTIFIER, + accessLocation GeneralName } + +-- CRL number extension OID and syntax + +id-ce-cRLNumber OBJECT IDENTIFIER ::= { id-ce 20 } + +CRLNumber ::= INTEGER (0..MAX) + +-- issuing distribution point extension OID and syntax + +id-ce-issuingDistributionPoint OBJECT IDENTIFIER ::= { id-ce 28 } + +IssuingDistributionPoint ::= SEQUENCE { + distributionPoint [0] DistributionPointName OPTIONAL, + onlyContainsUserCerts [1] BOOLEAN DEFAULT FALSE, + onlyContainsCACerts [2] BOOLEAN DEFAULT FALSE, + onlySomeReasons [3] ReasonFlags OPTIONAL, + indirectCRL [4] BOOLEAN DEFAULT FALSE } + + +id-ce-deltaCRLIndicator OBJECT IDENTIFIER ::= { id-ce 27 } + +-- deltaCRLIndicator ::= BaseCRLNumber + +BaseCRLNumber ::= CRLNumber + +-- CRL reasons extension OID and syntax + +id-ce-cRLReasons OBJECT IDENTIFIER ::= { id-ce 21 } + +CRLReason ::= ENUMERATED { + unspecified (0), + keyCompromise (1), + cACompromise (2), + affiliationChanged (3), + superseded (4), + cessationOfOperation (5), + certificateHold (6), + removeFromCRL (8) } + +-- certificate issuer CRL entry extension OID and syntax + +id-ce-certificateIssuer OBJECT IDENTIFIER ::= { id-ce 29 } + +CertificateIssuer ::= GeneralNames + +-- hold instruction extension OID and syntax + +id-ce-holdInstructionCode OBJECT IDENTIFIER ::= { id-ce 23 } + +HoldInstructionCode ::= OBJECT IDENTIFIER + +-- ANSI x9 holdinstructions + +-- ANSI x9 arc holdinstruction arc +holdInstruction OBJECT IDENTIFIER ::= + {joint-iso-itu-t(2) member-body(2) us(840) x9cm(10040) 2} + +-- ANSI X9 holdinstructions referenced by this standard +id-holdinstruction-none OBJECT IDENTIFIER ::= + {holdInstruction 1} -- deprecated +id-holdinstruction-callissuer OBJECT IDENTIFIER ::= + {holdInstruction 2} +id-holdinstruction-reject OBJECT IDENTIFIER ::= + {holdInstruction 3} + +-- invalidity date CRL entry extension OID and syntax + +id-ce-invalidityDate OBJECT IDENTIFIER ::= { id-ce 24 } + +InvalidityDate ::= GeneralizedTime + + +-- -------------------------------------- +-- EXPLICIT +-- -------------------------------------- + +-- UNIVERSAL Types defined in '93 and '98 ASN.1 +-- but required by this specification + +VisibleString ::= [UNIVERSAL 26] IMPLICIT OCTET STRING + +NumericString ::= [UNIVERSAL 18] IMPLICIT OCTET STRING + +IA5String ::= [UNIVERSAL 22] IMPLICIT OCTET STRING + +TeletexString ::= [UNIVERSAL 20] IMPLICIT OCTET STRING + +PrintableString ::= [UNIVERSAL 19] IMPLICIT OCTET STRING + +UniversalString ::= [UNIVERSAL 28] IMPLICIT OCTET STRING + -- UniversalString is defined in ASN.1:1993 + +BMPString ::= [UNIVERSAL 30] IMPLICIT OCTET STRING + -- BMPString is the subtype of UniversalString and models + -- the Basic Multilingual Plane of ISO/IEC/ITU 10646-1 + +UTF8String ::= [UNIVERSAL 12] IMPLICIT OCTET STRING + -- The content of this type conforms to RFC 2279. + + +-- PKIX specific OIDs + +id-pkix OBJECT IDENTIFIER ::= + { iso(1) identified-organization(3) dod(6) internet(1) + security(5) mechanisms(5) pkix(7) } + +-- PKIX arcs + +id-pe OBJECT IDENTIFIER ::= { id-pkix 1 } + -- arc for private certificate extensions +id-qt OBJECT IDENTIFIER ::= { id-pkix 2 } + -- arc for policy qualifier types +id-kp OBJECT IDENTIFIER ::= { id-pkix 3 } + -- arc for extended key purpose OIDS +id-ad OBJECT IDENTIFIER ::= { id-pkix 48 } + -- arc for access descriptors + +-- policyQualifierIds for Internet policy qualifiers + +id-qt-cps OBJECT IDENTIFIER ::= { id-qt 1 } + -- OID for CPS qualifier +id-qt-unotice OBJECT IDENTIFIER ::= { id-qt 2 } + -- OID for user notice qualifier + +-- access descriptor definitions + +id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 } +id-ad-caIssuers OBJECT IDENTIFIER ::= { id-ad 2 } + +-- attribute data types -- + +Attribute ::= SEQUENCE { + type AttributeType, + values SET OF AttributeValue + -- at least one value is required -- +} + +AttributeType ::= OBJECT IDENTIFIER + +AttributeValue ::= ANY DEFINED BY type + +AttributeTypeAndValue ::= SEQUENCE { + type AttributeType, + value AttributeValue } + +-- suggested naming attributes: Definition of the following +-- information object set may be augmented to meet local +-- requirements. Note that deleting members of the set may +-- prevent interoperability with conforming implementations. +-- presented in pairs: the AttributeType followed by the +-- type definition for the corresponding AttributeValue + +-- Arc for standard naming attributes +id-at OBJECT IDENTIFIER ::= {joint-iso-ccitt(2) ds(5) 4} + +-- Attributes of type NameDirectoryString +id-at-initials AttributeType ::= { id-at 43 } +X520initials ::= DirectoryString + +id-at-generationQualifier AttributeType ::= { id-at 44 } +X520generationQualifier ::= DirectoryString + +id-at-surname AttributeType ::= { id-at 4 } +X520surName ::= DirectoryString + +id-at-givenName AttributeType ::= { id-at 42 } +X520givenName ::= DirectoryString + +id-at-name AttributeType ::= { id-at 41 } +X520name ::= DirectoryString + +id-at-commonName AttributeType ::= {id-at 3} +X520CommonName ::= DirectoryString + +id-at-localityName AttributeType ::= {id-at 7} +X520LocalityName ::= DirectoryString + +id-at-stateOrProvinceName AttributeType ::= {id-at 8} +X520StateOrProvinceName ::= DirectoryString + +id-at-organizationName AttributeType ::= {id-at 10} +X520OrganizationName ::= DirectoryString + +id-at-organizationalUnitName AttributeType ::= {id-at 11} +X520OrganizationalUnitName ::= DirectoryString + +id-at-title AttributeType ::= {id-at 12} +X520Title ::= DirectoryString + +id-at-description AttributeType ::= {id-at 13} +X520Description ::= DirectoryString + +id-at-dnQualifier AttributeType ::= {id-at 46} +X520dnQualifier ::= PrintableString + +id-at-countryName AttributeType ::= {id-at 6} +X520countryName ::= PrintableString (SIZE (2)) -- IS 3166 codes + +id-at-serialNumber AttributeType ::= {id-at 5} +X520serialNumber ::= PrintableString + +id-at-telephoneNumber AttributeType ::= {id-at 20} +X520telephoneNumber ::= PrintableString + +id-at-facsimileTelephoneNumber AttributeType ::= {id-at 23} +X520facsimileTelephoneNumber ::= PrintableString + +id-at-pseudonym AttributeType ::= {id-at 65} +X520pseudonym ::= DirectoryString + +id-at-name AttributeType ::= {id-at 41} +X520name ::= DirectoryString + +id-at-streetAddress AttributeType ::= {id-at 9} +X520streetAddress ::= DirectoryString + +id-at-postalAddress AttributeType ::= {id-at 16} +X520postalAddress ::= PostalAddress + +PostalAddress ::= SEQUENCE OF DirectoryString + + + -- Legacy attributes + +pkcs OBJECT IDENTIFIER ::= + { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) } + +pkcs-9 OBJECT IDENTIFIER ::= + { pkcs 9 } + + +emailAddress AttributeType ::= { pkcs-9 1 } + +Pkcs9email ::= IA5String (SIZE (1..ub-emailaddress-length)) + +-- naming data types -- + +Name ::= CHOICE { -- only one possibility for now -- + rdnSequence RDNSequence } + +RDNSequence ::= SEQUENCE OF RelativeDistinguishedName + +DistinguishedName ::= RDNSequence + +RelativeDistinguishedName ::= + SET SIZE (1 .. MAX) OF AttributeTypeAndValue + + + +-- -------------------------------------------------------- +-- certificate and CRL specific structures begin here +-- -------------------------------------------------------- + +Certificate ::= SEQUENCE { + tbsCertificate TBSCertificate, + signatureAlgorithm AlgorithmIdentifier, + signature BIT STRING } + +TBSCertificate ::= SEQUENCE { + version [0] EXPLICIT Version DEFAULT v1, + serialNumber CertificateSerialNumber, + signature AlgorithmIdentifier, + issuer Name, + validity Validity, + subject Name, + subjectPublicKeyInfo SubjectPublicKeyInfo, + issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, + -- If present, version shall be v2 or v3 + subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL, + -- If present, version shall be v2 or v3 + extensions [3] EXPLICIT Extensions OPTIONAL + -- If present, version shall be v3 -- +} + +Version ::= INTEGER { v1(0), v2(1), v3(2) } + +CertificateSerialNumber ::= INTEGER + +Validity ::= SEQUENCE { + notBefore Time, + notAfter Time } + +Time ::= CHOICE { + utcTime UTCTime, + generalTime GeneralizedTime } + +UniqueIdentifier ::= BIT STRING + +SubjectPublicKeyInfo ::= SEQUENCE { + algorithm AlgorithmIdentifier, + subjectPublicKey BIT STRING } + +Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension + +Extension ::= SEQUENCE { + extnID OBJECT IDENTIFIER, + critical BOOLEAN DEFAULT FALSE, + extnValue OCTET STRING } + + +-- ------------------------------------------ +-- CRL structures +-- ------------------------------------------ + +CertificateList ::= SEQUENCE { + tbsCertList TBSCertList, + signatureAlgorithm AlgorithmIdentifier, + signature BIT STRING } + +TBSCertList ::= SEQUENCE { + version Version OPTIONAL, + -- if present, shall be v2 + signature AlgorithmIdentifier, + issuer Name, + thisUpdate Time, + nextUpdate Time OPTIONAL, + revokedCertificates SEQUENCE OF SEQUENCE { + userCertificate CertificateSerialNumber, + revocationDate Time, + crlEntryExtensions Extensions OPTIONAL + -- if present, shall be v2 + } OPTIONAL, + crlExtensions [0] EXPLICIT Extensions OPTIONAL + -- if present, shall be v2 -- +} + +-- Version, Time, CertificateSerialNumber, and Extensions were +-- defined earlier for use in the certificate structure + +AlgorithmIdentifier ::= SEQUENCE { + algorithm OBJECT IDENTIFIER, + parameters ANY DEFINED BY algorithm OPTIONAL } + -- contains a value of the type + -- registered for use with the + -- algorithm object identifier value + +-- Algorithm OIDs and parameter structures + +pkcs-1 OBJECT IDENTIFIER ::= { + pkcs 1 } + +rsaEncryption OBJECT IDENTIFIER ::= { pkcs-1 1 } + +md2WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 2 } + +md5WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 4 } + +sha1WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 5 } + +id-dsa-with-sha1 OBJECT IDENTIFIER ::= { + iso(1) member-body(2) us(840) x9-57 (10040) x9algorithm(4) 3 } + +Dss-Sig-Value ::= SEQUENCE { + r INTEGER, + s INTEGER +} + +dhpublicnumber OBJECT IDENTIFIER ::= { + iso(1) member-body(2) us(840) ansi-x942(10046) number-type(2) 1 } + +DomainParameters ::= SEQUENCE { + p INTEGER, -- odd prime, p=jq +1 + g INTEGER, -- generator, g + q INTEGER, -- factor of p-1 + j INTEGER OPTIONAL, -- subgroup factor, j>= 2 + validationParms ValidationParms OPTIONAL } + +ValidationParms ::= SEQUENCE { + seed BIT STRING, + pgenCounter INTEGER } + +id-dsa OBJECT IDENTIFIER ::= { + iso(1) member-body(2) us(840) x9-57(10040) x9algorithm(4) 1 } + +Dss-Parms ::= SEQUENCE { + p INTEGER, + q INTEGER, + g INTEGER } + +-- x400 address syntax starts here +-- OR Names + +ORAddress ::= SEQUENCE { + built-in-standard-attributes BuiltInStandardAttributes, + built-in-domain-defined-attributes + BuiltInDomainDefinedAttributes OPTIONAL, + -- see also teletex-domain-defined-attributes + extension-attributes ExtensionAttributes OPTIONAL } +-- The OR-address is semantically absent from the OR-name if the +-- built-in-standard-attribute sequence is empty and the +-- built-in-domain-defined-attributes and extension-attributes are +-- both omitted. + +-- Built-in Standard Attributes + +BuiltInStandardAttributes ::= SEQUENCE { + country-name CountryName OPTIONAL, + administration-domain-name AdministrationDomainName OPTIONAL, + network-address [0] EXPLICIT NetworkAddress OPTIONAL, + -- see also extended-network-address + terminal-identifier [1] EXPLICIT TerminalIdentifier OPTIONAL, + private-domain-name [2] EXPLICIT PrivateDomainName OPTIONAL, + organization-name [3] EXPLICIT OrganizationName OPTIONAL, + -- see also teletex-organization-name + numeric-user-identifier [4] EXPLICIT NumericUserIdentifier OPTIONAL, + personal-name [5] EXPLICIT PersonalName OPTIONAL, + -- see also teletex-personal-name + organizational-unit-names [6] EXPLICIT OrganizationalUnitNames OPTIONAL + -- see also teletex-organizational-unit-names -- +} + +CountryName ::= [APPLICATION 1] CHOICE { + x121-dcc-code NumericString + (SIZE (ub-country-name-numeric-length)), + iso-3166-alpha2-code PrintableString + (SIZE (ub-country-name-alpha-length)) } + +AdministrationDomainName ::= [APPLICATION 2] EXPLICIT CHOICE { + numeric NumericString (SIZE (0..ub-domain-name-length)), + printable PrintableString (SIZE (0..ub-domain-name-length)) } + +NetworkAddress ::= X121Address -- see also extended-network-address + +X121Address ::= NumericString (SIZE (1..ub-x121-address-length)) + +TerminalIdentifier ::= PrintableString (SIZE (1..ub-terminal-id-length)) + +PrivateDomainName ::= CHOICE { + numeric NumericString (SIZE (1..ub-domain-name-length)), + printable PrintableString (SIZE (1..ub-domain-name-length)) } + +OrganizationName ::= PrintableString + (SIZE (1..ub-organization-name-length)) +-- see also teletex-organization-name + +NumericUserIdentifier ::= NumericString + (SIZE (1..ub-numeric-user-id-length)) + +PersonalName ::= SET { + surname [0] PrintableString (SIZE (1..ub-surname-length)), + given-name [1] PrintableString + (SIZE (1..ub-given-name-length)) OPTIONAL, + initials [2] PrintableString (SIZE (1..ub-initials-length)) OPTIONAL, + generation-qualifier [3] PrintableString + (SIZE (1..ub-generation-qualifier-length)) OPTIONAL } +-- see also teletex-personal-name + +OrganizationalUnitNames ::= SEQUENCE SIZE (1..ub-organizational-units) + OF OrganizationalUnitName +-- see also teletex-organizational-unit-names + +OrganizationalUnitName ::= PrintableString (SIZE + (1..ub-organizational-unit-name-length)) + +-- Built-in Domain-defined Attributes + +BuiltInDomainDefinedAttributes ::= SEQUENCE SIZE + (1..ub-domain-defined-attributes) OF + BuiltInDomainDefinedAttribute + +BuiltInDomainDefinedAttribute ::= SEQUENCE { + type PrintableString (SIZE + (1..ub-domain-defined-attribute-type-length)), + value PrintableString (SIZE + (1..ub-domain-defined-attribute-value-length))} + +-- Extension Attributes + +ExtensionAttributes ::= SET SIZE (1..ub-extension-attributes) OF + ExtensionAttribute + +ExtensionAttribute ::= SEQUENCE { + extension-attribute-type [0] EXPLICIT INTEGER (0..ub-extension-attributes), + extension-attribute-value [1] EXPLICIT + ANY DEFINED BY extension-attribute-type } + +-- Extension types and attribute values +-- + +common-name INTEGER ::= 1 + +CommonName ::= PrintableString (SIZE (1..ub-common-name-length)) + +teletex-common-name INTEGER ::= 2 + +TeletexCommonName ::= TeletexString (SIZE (1..ub-common-name-length)) + +teletex-organization-name INTEGER ::= 3 + +TeletexOrganizationName ::= + TeletexString (SIZE (1..ub-organization-name-length)) + +teletex-personal-name INTEGER ::= 4 + +TeletexPersonalName ::= SET { + surname [0] EXPLICIT TeletexString (SIZE (1..ub-surname-length)), + given-name [1] EXPLICIT TeletexString + (SIZE (1..ub-given-name-length)) OPTIONAL, + initials [2] EXPLICIT TeletexString (SIZE (1..ub-initials-length)) OPTIONAL, + generation-qualifier [3] EXPLICIT TeletexString (SIZE + (1..ub-generation-qualifier-length)) OPTIONAL } + +teletex-organizational-unit-names INTEGER ::= 5 + +TeletexOrganizationalUnitNames ::= SEQUENCE SIZE + (1..ub-organizational-units) OF TeletexOrganizationalUnitName + +TeletexOrganizationalUnitName ::= TeletexString + (SIZE (1..ub-organizational-unit-name-length)) + +pds-name INTEGER ::= 7 + +PDSName ::= PrintableString (SIZE (1..ub-pds-name-length)) + +physical-delivery-country-name INTEGER ::= 8 + +PhysicalDeliveryCountryName ::= CHOICE { + x121-dcc-code NumericString (SIZE (ub-country-name-numeric-length)), + iso-3166-alpha2-code PrintableString + (SIZE (ub-country-name-alpha-length)) } + +postal-code INTEGER ::= 9 + +PostalCode ::= CHOICE { + numeric-code NumericString (SIZE (1..ub-postal-code-length)), + printable-code PrintableString (SIZE (1..ub-postal-code-length)) } + +physical-delivery-office-name INTEGER ::= 10 + +PhysicalDeliveryOfficeName ::= PDSParameter + +physical-delivery-office-number INTEGER ::= 11 + +PhysicalDeliveryOfficeNumber ::= PDSParameter + +extension-OR-address-components INTEGER ::= 12 + +ExtensionORAddressComponents ::= PDSParameter + +physical-delivery-personal-name INTEGER ::= 13 + +PhysicalDeliveryPersonalName ::= PDSParameter + +physical-delivery-organization-name INTEGER ::= 14 + +PhysicalDeliveryOrganizationName ::= PDSParameter + +extension-physical-delivery-address-components INTEGER ::= 15 + +ExtensionPhysicalDeliveryAddressComponents ::= PDSParameter + +unformatted-postal-address INTEGER ::= 16 + +UnformattedPostalAddress ::= SET { + printable-address SEQUENCE SIZE (1..ub-pds-physical-address-lines) OF + PrintableString (SIZE (1..ub-pds-parameter-length)) OPTIONAL, + teletex-string TeletexString + (SIZE (1..ub-unformatted-address-length)) OPTIONAL } + +street-address INTEGER ::= 17 + +StreetAddress ::= PDSParameter + +post-office-box-address INTEGER ::= 18 + +PostOfficeBoxAddress ::= PDSParameter + +poste-restante-address INTEGER ::= 19 + +PosteRestanteAddress ::= PDSParameter + +unique-postal-name INTEGER ::= 20 + +UniquePostalName ::= PDSParameter + +local-postal-attributes INTEGER ::= 21 + +LocalPostalAttributes ::= PDSParameter + +PDSParameter ::= SET { + printable-string PrintableString + (SIZE(1..ub-pds-parameter-length)) OPTIONAL, + teletex-string TeletexString + (SIZE(1..ub-pds-parameter-length)) OPTIONAL } + +extended-network-address INTEGER ::= 22 + +ExtendedNetworkAddress ::= CHOICE { + e163-4-address SEQUENCE { + number [0] EXPLICIT NumericString (SIZE (1..ub-e163-4-number-length)), + sub-address [1] EXPLICIT NumericString + (SIZE (1..ub-e163-4-sub-address-length)) OPTIONAL }, + psap-address [0] EXPLICIT PresentationAddress } + +PresentationAddress ::= SEQUENCE { + pSelector [0] EXPLICIT OCTET STRING OPTIONAL, + sSelector [1] EXPLICIT OCTET STRING OPTIONAL, + tSelector [2] EXPLICIT OCTET STRING OPTIONAL, + nAddresses [3] EXPLICIT SET SIZE (1..MAX) OF OCTET STRING } + +terminal-type INTEGER ::= 23 + +TerminalType ::= INTEGER { + telex (3), + teletex (4), + g3-facsimile (5), + g4-facsimile (6), + ia5-terminal (7), + videotex (8) } -- (0..ub-integer-options) + +-- Extension Domain-defined Attributes + +teletex-domain-defined-attributes INTEGER ::= 6 + +TeletexDomainDefinedAttributes ::= SEQUENCE SIZE + (1..ub-domain-defined-attributes) OF TeletexDomainDefinedAttribute + +TeletexDomainDefinedAttribute ::= SEQUENCE { + type TeletexString + (SIZE (1..ub-domain-defined-attribute-type-length)), + value TeletexString + (SIZE (1..ub-domain-defined-attribute-value-length)) } + +-- specifications of Upper Bounds shall be regarded as mandatory +-- from Annex B of ITU-T X.411 Reference Definition of MTS Parameter +-- Upper Bounds + +-- Upper Bounds +ub-name INTEGER ::= 32768 +ub-common-name INTEGER ::= 64 +ub-locality-name INTEGER ::= 128 +ub-state-name INTEGER ::= 128 +ub-organization-name INTEGER ::= 64 +ub-organizational-unit-name INTEGER ::= 64 +ub-title INTEGER ::= 64 +ub-match INTEGER ::= 128 + +ub-emailaddress-length INTEGER ::= 128 + +ub-common-name-length INTEGER ::= 64 +ub-country-name-alpha-length INTEGER ::= 2 +ub-country-name-numeric-length INTEGER ::= 3 +ub-domain-defined-attributes INTEGER ::= 4 +ub-domain-defined-attribute-type-length INTEGER ::= 8 +ub-domain-defined-attribute-value-length INTEGER ::= 128 +ub-domain-name-length INTEGER ::= 16 +ub-extension-attributes INTEGER ::= 256 +ub-e163-4-number-length INTEGER ::= 15 +ub-e163-4-sub-address-length INTEGER ::= 40 +ub-generation-qualifier-length INTEGER ::= 3 +ub-given-name-length INTEGER ::= 16 +ub-initials-length INTEGER ::= 5 +ub-integer-options INTEGER ::= 256 +ub-numeric-user-id-length INTEGER ::= 32 +ub-organization-name-length INTEGER ::= 64 +ub-organizational-unit-name-length INTEGER ::= 32 +ub-organizational-units INTEGER ::= 4 +ub-pds-name-length INTEGER ::= 16 +ub-pds-parameter-length INTEGER ::= 30 +ub-pds-physical-address-lines INTEGER ::= 6 +ub-postal-code-length INTEGER ::= 16 +ub-surname-length INTEGER ::= 40 +ub-terminal-id-length INTEGER ::= 24 +ub-unformatted-address-length INTEGER ::= 180 +ub-x121-address-length INTEGER ::= 16 + +-- Note - upper bounds on string types, such as TeletexString, are +-- measured in characters. Excepting PrintableString or IA5String, a +-- significantly greater number of octets will be required to hold +-- such a value. As a minimum, 16 octets, or twice the specified upper +-- bound, whichever is the larger, should be allowed for TeletexString. +-- For UTF8String or UniversalString at least four times the upper +-- bound should be allowed. + + + +-- END of PKIX1Implicit88 + + +-- BEGIN of RFC2630 + +-- Cryptographic Message Syntax + +pkcs-7-ContentInfo ::= SEQUENCE { + contentType pkcs-7-ContentType, + content [0] EXPLICIT ANY DEFINED BY contentType } + +pkcs-7-DigestInfo ::= SEQUENCE { + digestAlgorithm pkcs-7-DigestAlgorithmIdentifier, + digest pkcs-7-Digest +} + +pkcs-7-Digest ::= OCTET STRING + +pkcs-7-ContentType ::= OBJECT IDENTIFIER + +pkcs-7-SignedData ::= SEQUENCE { + version pkcs-7-CMSVersion, + digestAlgorithms pkcs-7-DigestAlgorithmIdentifiers, + encapContentInfo pkcs-7-EncapsulatedContentInfo, + certificates [0] IMPLICIT pkcs-7-CertificateSet OPTIONAL, + crls [1] IMPLICIT pkcs-7-CertificateRevocationLists OPTIONAL, + signerInfos pkcs-7-SignerInfos +} + +pkcs-7-CMSVersion ::= INTEGER { v0(0), v1(1), v2(2), v3(3), v4(4) } + +pkcs-7-DigestAlgorithmIdentifiers ::= SET OF pkcs-7-DigestAlgorithmIdentifier + +pkcs-7-DigestAlgorithmIdentifier ::= AlgorithmIdentifier + +pkcs-7-EncapsulatedContentInfo ::= SEQUENCE { + eContentType pkcs-7-ContentType, + eContent [0] EXPLICIT OCTET STRING OPTIONAL } + +-- We don't use CertificateList here since we only want +-- to read the raw data. +pkcs-7-CertificateRevocationLists ::= SET OF ANY + +pkcs-7-CertificateChoices ::= CHOICE { +-- Although the paper uses Certificate type, we +-- don't use it since, we don't need to parse it. +-- We only need to read and store it. + certificate ANY +} + +pkcs-7-CertificateSet ::= SET OF pkcs-7-CertificateChoices + +pkcs-7-SignerInfos ::= SET OF ANY -- this is not correct but we don't use it + -- anyway + + +-- BEGIN of RFC2986 + +-- Certificate requests +pkcs-10-CertificationRequestInfo ::= SEQUENCE { + version INTEGER { v1(0) }, + subject Name, + subjectPKInfo SubjectPublicKeyInfo, + attributes [0] Attributes +} + +Attributes ::= SET OF Attribute + +pkcs-10-CertificationRequest ::= SEQUENCE { + certificationRequestInfo pkcs-10-CertificationRequestInfo, + signatureAlgorithm AlgorithmIdentifier, + signature BIT STRING +} + +-- stuff from PKCS#9 + +pkcs-9-ub-challengePassword INTEGER ::= 255 + +pkcs-9-certTypes OBJECT IDENTIFIER ::= {pkcs-9 22} +pkcs-9-crlTypes OBJECT IDENTIFIER ::= {pkcs-9 23} + +pkcs-9-at-challengePassword OBJECT IDENTIFIER ::= {pkcs-9 7} + +pkcs-9-challengePassword ::= CHOICE { + printableString PrintableString (SIZE (1..pkcs-9-ub-challengePassword)), + utf8String UTF8String (SIZE (1..pkcs-9-ub-challengePassword)) } + +pkcs-9-at-localKeyId OBJECT IDENTIFIER ::= {pkcs-9 21} + +pkcs-9-localKeyId ::= OCTET STRING + +pkcs-9-at-friendlyName OBJECT IDENTIFIER ::= {pkcs-9 20} + +pkcs-9-friendlyName ::= BMPString (SIZE (1..255)) + +-- PKCS #8 stuff + +-- Private-key information syntax + +pkcs-8-PrivateKeyInfo ::= SEQUENCE { + version pkcs-8-Version, + privateKeyAlgorithm AlgorithmIdentifier, + privateKey pkcs-8-PrivateKey, + attributes [0] Attributes OPTIONAL } + +pkcs-8-Version ::= INTEGER {v1(0)} + +pkcs-8-PrivateKey ::= OCTET STRING + +pkcs-8-Attributes ::= SET OF Attribute + +-- Encrypted private-key information syntax + +pkcs-8-EncryptedPrivateKeyInfo ::= SEQUENCE { + encryptionAlgorithm AlgorithmIdentifier, + encryptedData pkcs-8-EncryptedData +} + +pkcs-8-EncryptedData ::= OCTET STRING + +-- PKCS #5 stuff + +pkcs-5 OBJECT IDENTIFIER ::= + { pkcs 5 } + +pkcs-5-encryptionAlgorithm OBJECT IDENTIFIER ::= + { iso(1) member-body(2) us(840) rsadsi(113549) 3 } + +pkcs-5-des-EDE3-CBC OBJECT IDENTIFIER ::= {pkcs-5-encryptionAlgorithm 7} + +pkcs-5-des-EDE3-CBC-params ::= OCTET STRING (SIZE(8)) + +pkcs-5-id-PBES2 OBJECT IDENTIFIER ::= {pkcs-5 13} + +pkcs-5-PBES2-params ::= SEQUENCE { + keyDerivationFunc AlgorithmIdentifier, + encryptionScheme AlgorithmIdentifier } + +-- PBKDF2 + +pkcs-5-id-PBKDF2 OBJECT IDENTIFIER ::= {pkcs-5 12} + +-- pkcs-5-id-hmacWithSHA1 OBJECT IDENTIFIER ::= {iso(1) member-body(2) us(840) rsadsi(113549) 2 7} + +-- pkcs-5-algid-hmacWithSHA1 AlgorithmIdentifier ::= +-- {algorithm pkcs-5-id-hmacWithSHA1, parameters NULL : NULL} + +pkcs-5-PBKDF2-params ::= SEQUENCE { + salt CHOICE { + specified OCTET STRING, + otherSource AlgorithmIdentifier + }, + iterationCount INTEGER (1..MAX), + keyLength INTEGER (1..MAX) OPTIONAL, + prf AlgorithmIdentifier OPTIONAL -- DEFAULT pkcs-5-id-hmacWithSHA1 +} + +-- PKCS #12 stuff + +pkcs-12 OBJECT IDENTIFIER ::= {pkcs 12} + +pkcs-12-PFX ::= SEQUENCE { + version INTEGER {v3(3)}, + authSafe pkcs-7-ContentInfo, + macData pkcs-12-MacData OPTIONAL +} + +pkcs-12-PbeParams ::= SEQUENCE { + salt OCTET STRING, + iterations INTEGER +} + +pkcs-12-MacData ::= SEQUENCE { + mac pkcs-7-DigestInfo, + macSalt OCTET STRING, + iterations INTEGER DEFAULT 1 +-- Note: The default is for historical reasons and its use is +-- deprecated. A higher value, like 1024 is recommended. +} + +pkcs-12-AuthenticatedSafe ::= SEQUENCE OF pkcs-7-ContentInfo + -- Data if unencrypted + -- EncryptedData if password-encrypted + -- EnvelopedData if public key-encrypted + +pkcs-12-SafeContents ::= SEQUENCE OF pkcs-12-SafeBag + +pkcs-12-SafeBag ::= SEQUENCE { + bagId OBJECT IDENTIFIER, + bagValue [0] EXPLICIT ANY DEFINED BY badId, + bagAttributes SET OF pkcs-12-PKCS12Attribute OPTIONAL +} + +-- Bag types + + +pkcs-12-bagtypes OBJECT IDENTIFIER ::= {pkcs-12 10 1} + +pkcs-12-keyBag OBJECT IDENTIFIER ::= {pkcs-12-bagtypes 1} +pkcs-12-pkcs8ShroudedKeyBag OBJECT IDENTIFIER ::= {pkcs-12-bagtypes 2} +pkcs-12-certBag OBJECT IDENTIFIER ::= {pkcs-12-bagtypes 3} +pkcs-12-crlBag OBJECT IDENTIFIER ::= {pkcs-12-bagtypes 4} + +pkcs-12-KeyBag ::= pkcs-8-PrivateKeyInfo + +-- Shrouded KeyBag + +pkcs-12-PKCS8ShroudedKeyBag ::= pkcs-8-EncryptedPrivateKeyInfo + +-- CertBag + +pkcs-12-CertBag ::= SEQUENCE { + certId OBJECT IDENTIFIER, + certValue [0] EXPLICIT ANY DEFINED BY certId +} + +-- x509Certificate BAG-TYPE ::= {OCTET STRING IDENTIFIED BY {pkcs-9-certTypes 1}} +-- DER-encoded X.509 certificate stored in OCTET STRING + +pkcs-12-CRLBag ::= SEQUENCE { + crlId OBJECT IDENTIFIER, + crlValue [0] EXPLICIT ANY DEFINED BY crlId +} + +-- x509CRL BAG-TYPE ::= +-- {OCTET STRING IDENTIFIED BY {pkcs-9-crlTypes 1}} +-- DER-encoded X.509 CRL stored in OCTET STRING + +pkcs-12-PKCS12Attribute ::= Attribute + +-- PKCS #7 stuff (needed in PKCS 12) + +pkcs-7-data OBJECT IDENTIFIER ::= { iso(1) member-body(2) + us(840) rsadsi(113549) pkcs(1) pkcs7(7) 1 } + +pkcs-7-encryptedData OBJECT IDENTIFIER ::= { iso(1) member-body(2) + us(840) rsadsi(113549) pkcs(1) pkcs7(7) 6 } + +pkcs-7-Data ::= OCTET STRING + +pkcs-7-EncryptedData ::= SEQUENCE { + version pkcs-7-CMSVersion, + encryptedContentInfo pkcs-7-EncryptedContentInfo, + unprotectedAttrs [1] IMPLICIT pkcs-7-UnprotectedAttributes OPTIONAL } + +pkcs-7-EncryptedContentInfo ::= SEQUENCE { + contentType pkcs-7-ContentType, + contentEncryptionAlgorithm pkcs-7-ContentEncryptionAlgorithmIdentifier, + encryptedContent [0] IMPLICIT pkcs-7-EncryptedContent OPTIONAL } + +pkcs-7-ContentEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier + +pkcs-7-EncryptedContent ::= OCTET STRING + +pkcs-7-UnprotectedAttributes ::= SET SIZE (1..MAX) OF Attribute + +-- LDAP stuff +-- may not be correct + +id-at-ldap-DC AttributeType ::= { 0 9 2342 19200300 100 1 25 } + +ldap-DC ::= IA5String + +id-at-ldap-UID AttributeType ::= { 0 9 2342 19200300 100 1 1 } + +ldap-UID ::= DirectoryString + +-- rfc3039 + +id-pda OBJECT IDENTIFIER ::= { id-pkix 9 } + +id-pda-dateOfBirth AttributeType ::= { id-pda 1 } +DateOfBirth ::= GeneralizedTime + +id-pda-placeOfBirth AttributeType ::= { id-pda 2 } +PlaceOfBirth ::= DirectoryString + +id-pda-gender AttributeType ::= { id-pda 3 } +Gender ::= PrintableString (SIZE(1)) + -- "M", "F", "m" or "f" + +id-pda-countryOfCitizenship AttributeType ::= { id-pda 4 } +CountryOfCitizenship ::= PrintableString (SIZE (2)) + -- ISO 3166 Country Code + +id-pda-countryOfResidence AttributeType ::= { id-pda 5 } +CountryOfResidence ::= PrintableString (SIZE (2)) + -- ISO 3166 Country Code + +-- rfc3820 + +id-pe-proxyCertInfo OBJECT IDENTIFIER ::= { id-pe 14 } + +id-ppl-inheritAll OBJECT IDENTIFIER ::= { id-pkix 21 1 } +id-ppl-independent OBJECT IDENTIFIER ::= { id-pkix 21 2 } + +ProxyCertInfo ::= SEQUENCE { + pCPathLenConstraint INTEGER (0..MAX) OPTIONAL, + proxyPolicy ProxyPolicy } + +ProxyPolicy ::= SEQUENCE { + policyLanguage OBJECT IDENTIFIER, + policy OCTET STRING OPTIONAL } + +-- rfc3920 section 5.1.1 + +id-on OBJECT IDENTIFIER ::= { id-pkix 8 } -- other name forms + +id-on-xmppAddr OBJECT IDENTIFIER ::= { id-on 5 } + +XmppAddr ::= UTF8String + +END diff --git a/src/daemon/https/tls/pkix_asn1_tab.c b/src/daemon/https/tls/pkix_asn1_tab.c new file mode 100644 index 00000000..3370bb46 --- /dev/null +++ b/src/daemon/https/tls/pkix_asn1_tab.c @@ -0,0 +1,1130 @@ +#if HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +extern const ASN1_ARRAY_TYPE pkix_asn1_tab[] = { + {"PKIX1", 536875024, 0}, + {0, 1073741836, 0}, + {"id-ce", 1879048204, 0}, + {"joint-iso-ccitt", 1073741825, "2"}, + {"ds", 1073741825, "5"}, + {0, 1, "29"}, + {"id-ce-authorityKeyIdentifier", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "35"}, + {"AuthorityKeyIdentifier", 1610612741, 0}, + {"keyIdentifier", 1610637314, "KeyIdentifier"}, + {0, 4104, "0"}, + {"authorityCertIssuer", 1610637314, "GeneralNames"}, + {0, 4104, "1"}, + {"authorityCertSerialNumber", 536895490, "CertificateSerialNumber"}, + {0, 4104, "2"}, + {"KeyIdentifier", 1073741831, 0}, + {"id-ce-subjectKeyIdentifier", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "14"}, + {"SubjectKeyIdentifier", 1073741826, "KeyIdentifier"}, + {"id-ce-keyUsage", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "15"}, + {"KeyUsage", 1610874886, 0}, + {"digitalSignature", 1073741825, "0"}, + {"nonRepudiation", 1073741825, "1"}, + {"keyEncipherment", 1073741825, "2"}, + {"dataEncipherment", 1073741825, "3"}, + {"keyAgreement", 1073741825, "4"}, + {"keyCertSign", 1073741825, "5"}, + {"cRLSign", 1073741825, "6"}, + {"encipherOnly", 1073741825, "7"}, + {"decipherOnly", 1, "8"}, + {"id-ce-privateKeyUsagePeriod", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "16"}, + {"PrivateKeyUsagePeriod", 1610612741, 0}, + {"notBefore", 1619025937, 0}, + {0, 4104, "0"}, + {"notAfter", 545284113, 0}, + {0, 4104, "1"}, + {"id-ce-certificatePolicies", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "32"}, + {"CertificatePolicies", 1612709899, 0}, + {"MAX", 1074266122, "1"}, + {0, 2, "PolicyInformation"}, + {"PolicyInformation", 1610612741, 0}, + {"policyIdentifier", 1073741826, "CertPolicyId"}, + {"policyQualifiers", 538984459, 0}, + {"MAX", 1074266122, "1"}, + {0, 2, "PolicyQualifierInfo"}, + {"CertPolicyId", 1073741836, 0}, + {"PolicyQualifierInfo", 1610612741, 0}, + {"policyQualifierId", 1073741826, "PolicyQualifierId"}, + {"qualifier", 541065229, 0}, + {"policyQualifierId", 1, 0}, + {"PolicyQualifierId", 1073741836, 0}, + {"CPSuri", 1073741826, "IA5String"}, + {"UserNotice", 1610612741, 0}, + {"noticeRef", 1073758210, "NoticeReference"}, + {"explicitText", 16386, "DisplayText"}, + {"NoticeReference", 1610612741, 0}, + {"organization", 1073741826, "DisplayText"}, + {"noticeNumbers", 536870923, 0}, + {0, 3, 0}, + {"DisplayText", 1610612754, 0}, + {"visibleString", 1612709890, "VisibleString"}, + {"200", 524298, "1"}, + {"bmpString", 1612709890, "BMPString"}, + {"200", 524298, "1"}, + {"utf8String", 538968066, "UTF8String"}, + {"200", 524298, "1"}, + {"id-ce-policyMappings", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "33"}, + {"PolicyMappings", 1612709899, 0}, + {"MAX", 1074266122, "1"}, + {0, 536870917, 0}, + {"issuerDomainPolicy", 1073741826, "CertPolicyId"}, + {"subjectDomainPolicy", 2, "CertPolicyId"}, + {"DirectoryString", 1610612754, 0}, + {"teletexString", 1612709890, "TeletexString"}, + {"MAX", 524298, "1"}, + {"printableString", 1612709890, "PrintableString"}, + {"MAX", 524298, "1"}, + {"universalString", 1612709890, "UniversalString"}, + {"MAX", 524298, "1"}, + {"utf8String", 1612709890, "UTF8String"}, + {"MAX", 524298, "1"}, + {"bmpString", 1612709890, "BMPString"}, + {"MAX", 524298, "1"}, + {"ia5String", 538968066, "IA5String"}, + {"MAX", 524298, "1"}, + {"id-ce-subjectAltName", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "17"}, + {"SubjectAltName", 1073741826, "GeneralNames"}, + {"GeneralNames", 1612709899, 0}, + {"MAX", 1074266122, "1"}, + {0, 2, "GeneralName"}, + {"GeneralName", 1610612754, 0}, + {"otherName", 1610620930, "AnotherName"}, + {0, 4104, "0"}, + {"rfc822Name", 1610620930, "IA5String"}, + {0, 4104, "1"}, + {"dNSName", 1610620930, "IA5String"}, + {0, 4104, "2"}, + {"x400Address", 1610620930, "ORAddress"}, + {0, 4104, "3"}, + {"directoryName", 1610620930, "RDNSequence"}, + {0, 2056, "4"}, + {"ediPartyName", 1610620930, "EDIPartyName"}, + {0, 4104, "5"}, + {"uniformResourceIdentifier", 1610620930, "IA5String"}, + {0, 4104, "6"}, + {"iPAddress", 1610620935, 0}, + {0, 4104, "7"}, + {"registeredID", 536879116, 0}, + {0, 4104, "8"}, + {"AnotherName", 1610612741, 0}, + {"type-id", 1073741836, 0}, + {"value", 541073421, 0}, + {0, 1073743880, "0"}, + {"type-id", 1, 0}, + {"EDIPartyName", 1610612741, 0}, + {"nameAssigner", 1610637314, "DirectoryString"}, + {0, 4104, "0"}, + {"partyName", 536879106, "DirectoryString"}, + {0, 4104, "1"}, + {"id-ce-issuerAltName", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "18"}, + {"IssuerAltName", 1073741826, "GeneralNames"}, + {"id-ce-subjectDirectoryAttributes", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "9"}, + {"SubjectDirectoryAttributes", 1612709899, 0}, + {"MAX", 1074266122, "1"}, + {0, 2, "Attribute"}, + {"id-ce-basicConstraints", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "19"}, + {"BasicConstraints", 1610612741, 0}, + {"cA", 1610645508, 0}, + {0, 131081, 0}, + {"pathLenConstraint", 537411587, 0}, + {"0", 10, "MAX"}, + {"id-ce-nameConstraints", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "30"}, + {"NameConstraints", 1610612741, 0}, + {"permittedSubtrees", 1610637314, "GeneralSubtrees"}, + {0, 4104, "0"}, + {"excludedSubtrees", 536895490, "GeneralSubtrees"}, + {0, 4104, "1"}, + {"GeneralSubtrees", 1612709899, 0}, + {"MAX", 1074266122, "1"}, + {0, 2, "GeneralSubtree"}, + {"GeneralSubtree", 1610612741, 0}, + {"base", 1073741826, "GeneralName"}, + {"minimum", 1610653698, "BaseDistance"}, + {0, 1073741833, "0"}, + {0, 4104, "0"}, + {"maximum", 536895490, "BaseDistance"}, + {0, 4104, "1"}, + {"BaseDistance", 1611137027, 0}, + {"0", 10, "MAX"}, + {"id-ce-policyConstraints", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "36"}, + {"PolicyConstraints", 1610612741, 0}, + {"requireExplicitPolicy", 1610637314, "SkipCerts"}, + {0, 4104, "0"}, + {"inhibitPolicyMapping", 536895490, "SkipCerts"}, + {0, 4104, "1"}, + {"SkipCerts", 1611137027, 0}, + {"0", 10, "MAX"}, + {"id-ce-cRLDistributionPoints", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "31"}, + {"CRLDistributionPoints", 1612709899, 0}, + {"MAX", 1074266122, "1"}, + {0, 2, "DistributionPoint"}, + {"DistributionPoint", 1610612741, 0}, + {"distributionPoint", 1610637314, "DistributionPointName"}, + {0, 2056, "0"}, + {"reasons", 1610637314, "ReasonFlags"}, + {0, 4104, "1"}, + {"cRLIssuer", 536895490, "GeneralNames"}, + {0, 4104, "2"}, + {"DistributionPointName", 1610612754, 0}, + {"fullName", 1610620930, "GeneralNames"}, + {0, 4104, "0"}, + {"nameRelativeToCRLIssuer", 536879106, "RelativeDistinguishedName"}, + {0, 4104, "1"}, + {"ReasonFlags", 1610874886, 0}, + {"unused", 1073741825, "0"}, + {"keyCompromise", 1073741825, "1"}, + {"cACompromise", 1073741825, "2"}, + {"affiliationChanged", 1073741825, "3"}, + {"superseded", 1073741825, "4"}, + {"cessationOfOperation", 1073741825, "5"}, + {"certificateHold", 1073741825, "6"}, + {"privilegeWithdrawn", 1073741825, "7"}, + {"aACompromise", 1, "8"}, + {"id-ce-extKeyUsage", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "37"}, + {"ExtKeyUsageSyntax", 1612709899, 0}, + {"MAX", 1074266122, "1"}, + {0, 2, "KeyPurposeId"}, + {"KeyPurposeId", 1073741836, 0}, + {"id-kp-serverAuth", 1879048204, 0}, + {0, 1073741825, "id-kp"}, + {0, 1, "1"}, + {"id-kp-clientAuth", 1879048204, 0}, + {0, 1073741825, "id-kp"}, + {0, 1, "2"}, + {"id-kp-codeSigning", 1879048204, 0}, + {0, 1073741825, "id-kp"}, + {0, 1, "3"}, + {"id-kp-emailProtection", 1879048204, 0}, + {0, 1073741825, "id-kp"}, + {0, 1, "4"}, + {"id-kp-ipsecEndSystem", 1879048204, 0}, + {0, 1073741825, "id-kp"}, + {0, 1, "5"}, + {"id-kp-ipsecTunnel", 1879048204, 0}, + {0, 1073741825, "id-kp"}, + {0, 1, "6"}, + {"id-kp-ipsecUser", 1879048204, 0}, + {0, 1073741825, "id-kp"}, + {0, 1, "7"}, + {"id-kp-timeStamping", 1879048204, 0}, + {0, 1073741825, "id-kp"}, + {0, 1, "8"}, + {"id-pe-authorityInfoAccess", 1879048204, 0}, + {0, 1073741825, "id-pe"}, + {0, 1, "1"}, + {"AuthorityInfoAccessSyntax", 1612709899, 0}, + {"MAX", 1074266122, "1"}, + {0, 2, "AccessDescription"}, + {"AccessDescription", 1610612741, 0}, + {"accessMethod", 1073741836, 0}, + {"accessLocation", 2, "GeneralName"}, + {"id-ce-cRLNumber", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "20"}, + {"CRLNumber", 1611137027, 0}, + {"0", 10, "MAX"}, + {"id-ce-issuingDistributionPoint", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "28"}, + {"IssuingDistributionPoint", 1610612741, 0}, + {"distributionPoint", 1610637314, "DistributionPointName"}, + {0, 4104, "0"}, + {"onlyContainsUserCerts", 1610653700, 0}, + {0, 1073872905, 0}, + {0, 4104, "1"}, + {"onlyContainsCACerts", 1610653700, 0}, + {0, 1073872905, 0}, + {0, 4104, "2"}, + {"onlySomeReasons", 1610637314, "ReasonFlags"}, + {0, 4104, "3"}, + {"indirectCRL", 536911876, 0}, + {0, 1073872905, 0}, + {0, 4104, "4"}, + {"id-ce-deltaCRLIndicator", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "27"}, + {"BaseCRLNumber", 1073741826, "CRLNumber"}, + {"id-ce-cRLReasons", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "21"}, + {"CRLReason", 1610874901, 0}, + {"unspecified", 1073741825, "0"}, + {"keyCompromise", 1073741825, "1"}, + {"cACompromise", 1073741825, "2"}, + {"affiliationChanged", 1073741825, "3"}, + {"superseded", 1073741825, "4"}, + {"cessationOfOperation", 1073741825, "5"}, + {"certificateHold", 1073741825, "6"}, + {"removeFromCRL", 1, "8"}, + {"id-ce-certificateIssuer", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "29"}, + {"CertificateIssuer", 1073741826, "GeneralNames"}, + {"id-ce-holdInstructionCode", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "23"}, + {"HoldInstructionCode", 1073741836, 0}, + {"holdInstruction", 1879048204, 0}, + {"joint-iso-itu-t", 1073741825, "2"}, + {"member-body", 1073741825, "2"}, + {"us", 1073741825, "840"}, + {"x9cm", 1073741825, "10040"}, + {0, 1, "2"}, + {"id-holdinstruction-none", 1879048204, 0}, + {0, 1073741825, "holdInstruction"}, + {0, 1, "1"}, + {"id-holdinstruction-callissuer", 1879048204, 0}, + {0, 1073741825, "holdInstruction"}, + {0, 1, "2"}, + {"id-holdinstruction-reject", 1879048204, 0}, + {0, 1073741825, "holdInstruction"}, + {0, 1, "3"}, + {"id-ce-invalidityDate", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "24"}, + {"InvalidityDate", 1082130449, 0}, + {"VisibleString", 1610620935, 0}, + {0, 4360, "26"}, + {"NumericString", 1610620935, 0}, + {0, 4360, "18"}, + {"IA5String", 1610620935, 0}, + {0, 4360, "22"}, + {"TeletexString", 1610620935, 0}, + {0, 4360, "20"}, + {"PrintableString", 1610620935, 0}, + {0, 4360, "19"}, + {"UniversalString", 1610620935, 0}, + {0, 4360, "28"}, + {"BMPString", 1610620935, 0}, + {0, 4360, "30"}, + {"UTF8String", 1610620935, 0}, + {0, 4360, "12"}, + {"id-pkix", 1879048204, 0}, + {"iso", 1073741825, "1"}, + {"identified-organization", 1073741825, "3"}, + {"dod", 1073741825, "6"}, + {"internet", 1073741825, "1"}, + {"security", 1073741825, "5"}, + {"mechanisms", 1073741825, "5"}, + {"pkix", 1, "7"}, + {"id-pe", 1879048204, 0}, + {0, 1073741825, "id-pkix"}, + {0, 1, "1"}, + {"id-qt", 1879048204, 0}, + {0, 1073741825, "id-pkix"}, + {0, 1, "2"}, + {"id-kp", 1879048204, 0}, + {0, 1073741825, "id-pkix"}, + {0, 1, "3"}, + {"id-ad", 1879048204, 0}, + {0, 1073741825, "id-pkix"}, + {0, 1, "48"}, + {"id-qt-cps", 1879048204, 0}, + {0, 1073741825, "id-qt"}, + {0, 1, "1"}, + {"id-qt-unotice", 1879048204, 0}, + {0, 1073741825, "id-qt"}, + {0, 1, "2"}, + {"id-ad-ocsp", 1879048204, 0}, + {0, 1073741825, "id-ad"}, + {0, 1, "1"}, + {"id-ad-caIssuers", 1879048204, 0}, + {0, 1073741825, "id-ad"}, + {0, 1, "2"}, + {"Attribute", 1610612741, 0}, + {"type", 1073741826, "AttributeType"}, + {"values", 536870927, 0}, + {0, 2, "AttributeValue"}, + {"AttributeType", 1073741836, 0}, + {"AttributeValue", 1614807053, 0}, + {"type", 1, 0}, + {"AttributeTypeAndValue", 1610612741, 0}, + {"type", 1073741826, "AttributeType"}, + {"value", 2, "AttributeValue"}, + {"id-at", 1879048204, 0}, + {"joint-iso-ccitt", 1073741825, "2"}, + {"ds", 1073741825, "5"}, + {0, 1, "4"}, + {"id-at-initials", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "43"}, + {"X520initials", 1073741826, "DirectoryString"}, + {"id-at-generationQualifier", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "44"}, + {"X520generationQualifier", 1073741826, "DirectoryString"}, + {"id-at-surname", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "4"}, + {"X520surName", 1073741826, "DirectoryString"}, + {"id-at-givenName", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "42"}, + {"X520givenName", 1073741826, "DirectoryString"}, + {"id-at-name", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "41"}, + {"X520name", 1073741826, "DirectoryString"}, + {"id-at-commonName", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "3"}, + {"X520CommonName", 1073741826, "DirectoryString"}, + {"id-at-localityName", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "7"}, + {"X520LocalityName", 1073741826, "DirectoryString"}, + {"id-at-stateOrProvinceName", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "8"}, + {"X520StateOrProvinceName", 1073741826, "DirectoryString"}, + {"id-at-organizationName", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "10"}, + {"X520OrganizationName", 1073741826, "DirectoryString"}, + {"id-at-organizationalUnitName", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "11"}, + {"X520OrganizationalUnitName", 1073741826, "DirectoryString"}, + {"id-at-title", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "12"}, + {"X520Title", 1073741826, "DirectoryString"}, + {"id-at-description", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "13"}, + {"X520Description", 1073741826, "DirectoryString"}, + {"id-at-dnQualifier", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "46"}, + {"X520dnQualifier", 1073741826, "PrintableString"}, + {"id-at-countryName", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "6"}, + {"X520countryName", 1612709890, "PrintableString"}, + {0, 1048586, "2"}, + {"id-at-serialNumber", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "5"}, + {"X520serialNumber", 1073741826, "PrintableString"}, + {"id-at-telephoneNumber", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "20"}, + {"X520telephoneNumber", 1073741826, "PrintableString"}, + {"id-at-facsimileTelephoneNumber", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "23"}, + {"X520facsimileTelephoneNumber", 1073741826, "PrintableString"}, + {"id-at-pseudonym", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "65"}, + {"X520pseudonym", 1073741826, "DirectoryString"}, + {"id-at-name", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "41"}, + {"X520name", 1073741826, "DirectoryString"}, + {"id-at-streetAddress", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "9"}, + {"X520streetAddress", 1073741826, "DirectoryString"}, + {"id-at-postalAddress", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "16"}, + {"X520postalAddress", 1073741826, "PostalAddress"}, + {"PostalAddress", 1610612747, 0}, + {0, 2, "DirectoryString"}, + {"pkcs", 1879048204, 0}, + {"iso", 1073741825, "1"}, + {"member-body", 1073741825, "2"}, + {"us", 1073741825, "840"}, + {"rsadsi", 1073741825, "113549"}, + {"pkcs", 1, "1"}, + {"pkcs-9", 1879048204, 0}, + {0, 1073741825, "pkcs"}, + {0, 1, "9"}, + {"emailAddress", 1880096780, "AttributeType"}, + {0, 1073741825, "pkcs-9"}, + {0, 1, "1"}, + {"Pkcs9email", 1612709890, "IA5String"}, + {"ub-emailaddress-length", 524298, "1"}, + {"Name", 1610612754, 0}, + {"rdnSequence", 2, "RDNSequence"}, + {"RDNSequence", 1610612747, 0}, + {0, 2, "RelativeDistinguishedName"}, + {"DistinguishedName", 1073741826, "RDNSequence"}, + {"RelativeDistinguishedName", 1612709903, 0}, + {"MAX", 1074266122, "1"}, + {0, 2, "AttributeTypeAndValue"}, + {"Certificate", 1610612741, 0}, + {"tbsCertificate", 1073741826, "TBSCertificate"}, + {"signatureAlgorithm", 1073741826, "AlgorithmIdentifier"}, + {"signature", 6, 0}, + {"TBSCertificate", 1610612741, 0}, + {"version", 1610653698, "Version"}, + {0, 1073741833, "v1"}, + {0, 2056, "0"}, + {"serialNumber", 1073741826, "CertificateSerialNumber"}, + {"signature", 1073741826, "AlgorithmIdentifier"}, + {"issuer", 1073741826, "Name"}, + {"validity", 1073741826, "Validity"}, + {"subject", 1073741826, "Name"}, + {"subjectPublicKeyInfo", 1073741826, "SubjectPublicKeyInfo"}, + {"issuerUniqueID", 1610637314, "UniqueIdentifier"}, + {0, 4104, "1"}, + {"subjectUniqueID", 1610637314, "UniqueIdentifier"}, + {0, 4104, "2"}, + {"extensions", 536895490, "Extensions"}, + {0, 2056, "3"}, + {"Version", 1610874883, 0}, + {"v1", 1073741825, "0"}, + {"v2", 1073741825, "1"}, + {"v3", 1, "2"}, + {"CertificateSerialNumber", 1073741827, 0}, + {"Validity", 1610612741, 0}, + {"notBefore", 1073741826, "Time"}, + {"notAfter", 2, "Time"}, + {"Time", 1610612754, 0}, + {"utcTime", 1090519057, 0}, + {"generalTime", 8388625, 0}, + {"UniqueIdentifier", 1073741830, 0}, + {"SubjectPublicKeyInfo", 1610612741, 0}, + {"algorithm", 1073741826, "AlgorithmIdentifier"}, + {"subjectPublicKey", 6, 0}, + {"Extensions", 1612709899, 0}, + {"MAX", 1074266122, "1"}, + {0, 2, "Extension"}, + {"Extension", 1610612741, 0}, + {"extnID", 1073741836, 0}, + {"critical", 1610645508, 0}, + {0, 131081, 0}, + {"extnValue", 7, 0}, + {"CertificateList", 1610612741, 0}, + {"tbsCertList", 1073741826, "TBSCertList"}, + {"signatureAlgorithm", 1073741826, "AlgorithmIdentifier"}, + {"signature", 6, 0}, + {"TBSCertList", 1610612741, 0}, + {"version", 1073758210, "Version"}, + {"signature", 1073741826, "AlgorithmIdentifier"}, + {"issuer", 1073741826, "Name"}, + {"thisUpdate", 1073741826, "Time"}, + {"nextUpdate", 1073758210, "Time"}, + {"revokedCertificates", 1610629131, 0}, + {0, 536870917, 0}, + {"userCertificate", 1073741826, "CertificateSerialNumber"}, + {"revocationDate", 1073741826, "Time"}, + {"crlEntryExtensions", 16386, "Extensions"}, + {"crlExtensions", 536895490, "Extensions"}, + {0, 2056, "0"}, + {"AlgorithmIdentifier", 1610612741, 0}, + {"algorithm", 1073741836, 0}, + {"parameters", 541081613, 0}, + {"algorithm", 1, 0}, + {"pkcs-1", 1879048204, 0}, + {0, 1073741825, "pkcs"}, + {0, 1, "1"}, + {"rsaEncryption", 1879048204, 0}, + {0, 1073741825, "pkcs-1"}, + {0, 1, "1"}, + {"md2WithRSAEncryption", 1879048204, 0}, + {0, 1073741825, "pkcs-1"}, + {0, 1, "2"}, + {"md5WithRSAEncryption", 1879048204, 0}, + {0, 1073741825, "pkcs-1"}, + {0, 1, "4"}, + {"sha1WithRSAEncryption", 1879048204, 0}, + {0, 1073741825, "pkcs-1"}, + {0, 1, "5"}, + {"id-dsa-with-sha1", 1879048204, 0}, + {"iso", 1073741825, "1"}, + {"member-body", 1073741825, "2"}, + {"us", 1073741825, "840"}, + {"x9-57", 1073741825, "10040"}, + {"x9algorithm", 1073741825, "4"}, + {0, 1, "3"}, + {"Dss-Sig-Value", 1610612741, 0}, + {"r", 1073741827, 0}, + {"s", 3, 0}, + {"dhpublicnumber", 1879048204, 0}, + {"iso", 1073741825, "1"}, + {"member-body", 1073741825, "2"}, + {"us", 1073741825, "840"}, + {"ansi-x942", 1073741825, "10046"}, + {"number-type", 1073741825, "2"}, + {0, 1, "1"}, + {"DomainParameters", 1610612741, 0}, + {"p", 1073741827, 0}, + {"g", 1073741827, 0}, + {"q", 1073741827, 0}, + {"j", 1073758211, 0}, + {"validationParms", 16386, "ValidationParms"}, + {"ValidationParms", 1610612741, 0}, + {"seed", 1073741830, 0}, + {"pgenCounter", 3, 0}, + {"id-dsa", 1879048204, 0}, + {"iso", 1073741825, "1"}, + {"member-body", 1073741825, "2"}, + {"us", 1073741825, "840"}, + {"x9-57", 1073741825, "10040"}, + {"x9algorithm", 1073741825, "4"}, + {0, 1, "1"}, + {"Dss-Parms", 1610612741, 0}, + {"p", 1073741827, 0}, + {"q", 1073741827, 0}, + {"g", 3, 0}, + {"ORAddress", 1610612741, 0}, + {"built-in-standard-attributes", 1073741826, "BuiltInStandardAttributes"}, + {"built-in-domain-defined-attributes", 1073758210, + "BuiltInDomainDefinedAttributes"}, + {"extension-attributes", 16386, "ExtensionAttributes"}, + {"BuiltInStandardAttributes", 1610612741, 0}, + {"country-name", 1073758210, "CountryName"}, + {"administration-domain-name", 1073758210, "AdministrationDomainName"}, + {"network-address", 1610637314, "NetworkAddress"}, + {0, 2056, "0"}, + {"terminal-identifier", 1610637314, "TerminalIdentifier"}, + {0, 2056, "1"}, + {"private-domain-name", 1610637314, "PrivateDomainName"}, + {0, 2056, "2"}, + {"organization-name", 1610637314, "OrganizationName"}, + {0, 2056, "3"}, + {"numeric-user-identifier", 1610637314, "NumericUserIdentifier"}, + {0, 2056, "4"}, + {"personal-name", 1610637314, "PersonalName"}, + {0, 2056, "5"}, + {"organizational-unit-names", 536895490, "OrganizationalUnitNames"}, + {0, 2056, "6"}, + {"CountryName", 1610620946, 0}, + {0, 1073746952, "1"}, + {"x121-dcc-code", 1612709890, "NumericString"}, + {0, 1048586, "ub-country-name-numeric-length"}, + {"iso-3166-alpha2-code", 538968066, "PrintableString"}, + {0, 1048586, "ub-country-name-alpha-length"}, + {"AdministrationDomainName", 1610620946, 0}, + {0, 1073744904, "2"}, + {"numeric", 1612709890, "NumericString"}, + {"ub-domain-name-length", 524298, "0"}, + {"printable", 538968066, "PrintableString"}, + {"ub-domain-name-length", 524298, "0"}, + {"NetworkAddress", 1073741826, "X121Address"}, + {"X121Address", 1612709890, "NumericString"}, + {"ub-x121-address-length", 524298, "1"}, + {"TerminalIdentifier", 1612709890, "PrintableString"}, + {"ub-terminal-id-length", 524298, "1"}, + {"PrivateDomainName", 1610612754, 0}, + {"numeric", 1612709890, "NumericString"}, + {"ub-domain-name-length", 524298, "1"}, + {"printable", 538968066, "PrintableString"}, + {"ub-domain-name-length", 524298, "1"}, + {"OrganizationName", 1612709890, "PrintableString"}, + {"ub-organization-name-length", 524298, "1"}, + {"NumericUserIdentifier", 1612709890, "NumericString"}, + {"ub-numeric-user-id-length", 524298, "1"}, + {"PersonalName", 1610612750, 0}, + {"surname", 1814044674, "PrintableString"}, + {0, 1073745928, "0"}, + {"ub-surname-length", 524298, "1"}, + {"given-name", 1814061058, "PrintableString"}, + {0, 1073745928, "1"}, + {"ub-given-name-length", 524298, "1"}, + {"initials", 1814061058, "PrintableString"}, + {0, 1073745928, "2"}, + {"ub-initials-length", 524298, "1"}, + {"generation-qualifier", 740319234, "PrintableString"}, + {0, 1073745928, "3"}, + {"ub-generation-qualifier-length", 524298, "1"}, + {"OrganizationalUnitNames", 1612709899, 0}, + {"ub-organizational-units", 1074266122, "1"}, + {0, 2, "OrganizationalUnitName"}, + {"OrganizationalUnitName", 1612709890, "PrintableString"}, + {"ub-organizational-unit-name-length", 524298, "1"}, + {"BuiltInDomainDefinedAttributes", 1612709899, 0}, + {"ub-domain-defined-attributes", 1074266122, "1"}, + {0, 2, "BuiltInDomainDefinedAttribute"}, + {"BuiltInDomainDefinedAttribute", 1610612741, 0}, + {"type", 1612709890, "PrintableString"}, + {"ub-domain-defined-attribute-type-length", 524298, "1"}, + {"value", 538968066, "PrintableString"}, + {"ub-domain-defined-attribute-value-length", 524298, "1"}, + {"ExtensionAttributes", 1612709903, 0}, + {"ub-extension-attributes", 1074266122, "1"}, + {0, 2, "ExtensionAttribute"}, + {"ExtensionAttribute", 1610612741, 0}, + {"extension-attribute-type", 1611145219, 0}, + {0, 1073743880, "0"}, + {"0", 10, "ub-extension-attributes"}, + {"extension-attribute-value", 541073421, 0}, + {0, 1073743880, "1"}, + {"extension-attribute-type", 1, 0}, + {"common-name", 1342177283, "1"}, + {"CommonName", 1612709890, "PrintableString"}, + {"ub-common-name-length", 524298, "1"}, + {"teletex-common-name", 1342177283, "2"}, + {"TeletexCommonName", 1612709890, "TeletexString"}, + {"ub-common-name-length", 524298, "1"}, + {"teletex-organization-name", 1342177283, "3"}, + {"TeletexOrganizationName", 1612709890, "TeletexString"}, + {"ub-organization-name-length", 524298, "1"}, + {"teletex-personal-name", 1342177283, "4"}, + {"TeletexPersonalName", 1610612750, 0}, + {"surname", 1814044674, "TeletexString"}, + {0, 1073743880, "0"}, + {"ub-surname-length", 524298, "1"}, + {"given-name", 1814061058, "TeletexString"}, + {0, 1073743880, "1"}, + {"ub-given-name-length", 524298, "1"}, + {"initials", 1814061058, "TeletexString"}, + {0, 1073743880, "2"}, + {"ub-initials-length", 524298, "1"}, + {"generation-qualifier", 740319234, "TeletexString"}, + {0, 1073743880, "3"}, + {"ub-generation-qualifier-length", 524298, "1"}, + {"teletex-organizational-unit-names", 1342177283, "5"}, + {"TeletexOrganizationalUnitNames", 1612709899, 0}, + {"ub-organizational-units", 1074266122, "1"}, + {0, 2, "TeletexOrganizationalUnitName"}, + {"TeletexOrganizationalUnitName", 1612709890, "TeletexString"}, + {"ub-organizational-unit-name-length", 524298, "1"}, + {"pds-name", 1342177283, "7"}, + {"PDSName", 1612709890, "PrintableString"}, + {"ub-pds-name-length", 524298, "1"}, + {"physical-delivery-country-name", 1342177283, "8"}, + {"PhysicalDeliveryCountryName", 1610612754, 0}, + {"x121-dcc-code", 1612709890, "NumericString"}, + {0, 1048586, "ub-country-name-numeric-length"}, + {"iso-3166-alpha2-code", 538968066, "PrintableString"}, + {0, 1048586, "ub-country-name-alpha-length"}, + {"postal-code", 1342177283, "9"}, + {"PostalCode", 1610612754, 0}, + {"numeric-code", 1612709890, "NumericString"}, + {"ub-postal-code-length", 524298, "1"}, + {"printable-code", 538968066, "PrintableString"}, + {"ub-postal-code-length", 524298, "1"}, + {"physical-delivery-office-name", 1342177283, "10"}, + {"PhysicalDeliveryOfficeName", 1073741826, "PDSParameter"}, + {"physical-delivery-office-number", 1342177283, "11"}, + {"PhysicalDeliveryOfficeNumber", 1073741826, "PDSParameter"}, + {"extension-OR-address-components", 1342177283, "12"}, + {"ExtensionORAddressComponents", 1073741826, "PDSParameter"}, + {"physical-delivery-personal-name", 1342177283, "13"}, + {"PhysicalDeliveryPersonalName", 1073741826, "PDSParameter"}, + {"physical-delivery-organization-name", 1342177283, "14"}, + {"PhysicalDeliveryOrganizationName", 1073741826, "PDSParameter"}, + {"extension-physical-delivery-address-components", 1342177283, "15"}, + {"ExtensionPhysicalDeliveryAddressComponents", 1073741826, "PDSParameter"}, + {"unformatted-postal-address", 1342177283, "16"}, + {"UnformattedPostalAddress", 1610612750, 0}, + {"printable-address", 1814052875, 0}, + {"ub-pds-physical-address-lines", 1074266122, "1"}, + {0, 538968066, "PrintableString"}, + {"ub-pds-parameter-length", 524298, "1"}, + {"teletex-string", 740311042, "TeletexString"}, + {"ub-unformatted-address-length", 524298, "1"}, + {"street-address", 1342177283, "17"}, + {"StreetAddress", 1073741826, "PDSParameter"}, + {"post-office-box-address", 1342177283, "18"}, + {"PostOfficeBoxAddress", 1073741826, "PDSParameter"}, + {"poste-restante-address", 1342177283, "19"}, + {"PosteRestanteAddress", 1073741826, "PDSParameter"}, + {"unique-postal-name", 1342177283, "20"}, + {"UniquePostalName", 1073741826, "PDSParameter"}, + {"local-postal-attributes", 1342177283, "21"}, + {"LocalPostalAttributes", 1073741826, "PDSParameter"}, + {"PDSParameter", 1610612750, 0}, + {"printable-string", 1814052866, "PrintableString"}, + {"ub-pds-parameter-length", 524298, "1"}, + {"teletex-string", 740311042, "TeletexString"}, + {"ub-pds-parameter-length", 524298, "1"}, + {"extended-network-address", 1342177283, "22"}, + {"ExtendedNetworkAddress", 1610612754, 0}, + {"e163-4-address", 1610612741, 0}, + {"number", 1612718082, "NumericString"}, + {0, 1073743880, "0"}, + {"ub-e163-4-number-length", 524298, "1"}, + {"sub-address", 538992642, "NumericString"}, + {0, 1073743880, "1"}, + {"ub-e163-4-sub-address-length", 524298, "1"}, + {"psap-address", 536879106, "PresentationAddress"}, + {0, 2056, "0"}, + {"PresentationAddress", 1610612741, 0}, + {"pSelector", 1610637319, 0}, + {0, 2056, "0"}, + {"sSelector", 1610637319, 0}, + {0, 2056, "1"}, + {"tSelector", 1610637319, 0}, + {0, 2056, "2"}, + {"nAddresses", 538976271, 0}, + {0, 1073743880, "3"}, + {"MAX", 1074266122, "1"}, + {0, 7, 0}, + {"terminal-type", 1342177283, "23"}, + {"TerminalType", 1610874883, 0}, + {"telex", 1073741825, "3"}, + {"teletex", 1073741825, "4"}, + {"g3-facsimile", 1073741825, "5"}, + {"g4-facsimile", 1073741825, "6"}, + {"ia5-terminal", 1073741825, "7"}, + {"videotex", 1, "8"}, + {"teletex-domain-defined-attributes", 1342177283, "6"}, + {"TeletexDomainDefinedAttributes", 1612709899, 0}, + {"ub-domain-defined-attributes", 1074266122, "1"}, + {0, 2, "TeletexDomainDefinedAttribute"}, + {"TeletexDomainDefinedAttribute", 1610612741, 0}, + {"type", 1612709890, "TeletexString"}, + {"ub-domain-defined-attribute-type-length", 524298, "1"}, + {"value", 538968066, "TeletexString"}, + {"ub-domain-defined-attribute-value-length", 524298, "1"}, + {"ub-name", 1342177283, "32768"}, + {"ub-common-name", 1342177283, "64"}, + {"ub-locality-name", 1342177283, "128"}, + {"ub-state-name", 1342177283, "128"}, + {"ub-organization-name", 1342177283, "64"}, + {"ub-organizational-unit-name", 1342177283, "64"}, + {"ub-title", 1342177283, "64"}, + {"ub-match", 1342177283, "128"}, + {"ub-emailaddress-length", 1342177283, "128"}, + {"ub-common-name-length", 1342177283, "64"}, + {"ub-country-name-alpha-length", 1342177283, "2"}, + {"ub-country-name-numeric-length", 1342177283, "3"}, + {"ub-domain-defined-attributes", 1342177283, "4"}, + {"ub-domain-defined-attribute-type-length", 1342177283, "8"}, + {"ub-domain-defined-attribute-value-length", 1342177283, "128"}, + {"ub-domain-name-length", 1342177283, "16"}, + {"ub-extension-attributes", 1342177283, "256"}, + {"ub-e163-4-number-length", 1342177283, "15"}, + {"ub-e163-4-sub-address-length", 1342177283, "40"}, + {"ub-generation-qualifier-length", 1342177283, "3"}, + {"ub-given-name-length", 1342177283, "16"}, + {"ub-initials-length", 1342177283, "5"}, + {"ub-integer-options", 1342177283, "256"}, + {"ub-numeric-user-id-length", 1342177283, "32"}, + {"ub-organization-name-length", 1342177283, "64"}, + {"ub-organizational-unit-name-length", 1342177283, "32"}, + {"ub-organizational-units", 1342177283, "4"}, + {"ub-pds-name-length", 1342177283, "16"}, + {"ub-pds-parameter-length", 1342177283, "30"}, + {"ub-pds-physical-address-lines", 1342177283, "6"}, + {"ub-postal-code-length", 1342177283, "16"}, + {"ub-surname-length", 1342177283, "40"}, + {"ub-terminal-id-length", 1342177283, "24"}, + {"ub-unformatted-address-length", 1342177283, "180"}, + {"ub-x121-address-length", 1342177283, "16"}, + {"pkcs-7-ContentInfo", 1610612741, 0}, + {"contentType", 1073741826, "pkcs-7-ContentType"}, + {"content", 541073421, 0}, + {0, 1073743880, "0"}, + {"contentType", 1, 0}, + {"pkcs-7-DigestInfo", 1610612741, 0}, + {"digestAlgorithm", 1073741826, "pkcs-7-DigestAlgorithmIdentifier"}, + {"digest", 2, "pkcs-7-Digest"}, + {"pkcs-7-Digest", 1073741831, 0}, + {"pkcs-7-ContentType", 1073741836, 0}, + {"pkcs-7-SignedData", 1610612741, 0}, + {"version", 1073741826, "pkcs-7-CMSVersion"}, + {"digestAlgorithms", 1073741826, "pkcs-7-DigestAlgorithmIdentifiers"}, + {"encapContentInfo", 1073741826, "pkcs-7-EncapsulatedContentInfo"}, + {"certificates", 1610637314, "pkcs-7-CertificateSet"}, + {0, 4104, "0"}, + {"crls", 1610637314, "pkcs-7-CertificateRevocationLists"}, + {0, 4104, "1"}, + {"signerInfos", 2, "pkcs-7-SignerInfos"}, + {"pkcs-7-CMSVersion", 1610874883, 0}, + {"v0", 1073741825, "0"}, + {"v1", 1073741825, "1"}, + {"v2", 1073741825, "2"}, + {"v3", 1073741825, "3"}, + {"v4", 1, "4"}, + {"pkcs-7-DigestAlgorithmIdentifiers", 1610612751, 0}, + {0, 2, "pkcs-7-DigestAlgorithmIdentifier"}, + {"pkcs-7-DigestAlgorithmIdentifier", 1073741826, "AlgorithmIdentifier"}, + {"pkcs-7-EncapsulatedContentInfo", 1610612741, 0}, + {"eContentType", 1073741826, "pkcs-7-ContentType"}, + {"eContent", 536895495, 0}, + {0, 2056, "0"}, + {"pkcs-7-CertificateRevocationLists", 1610612751, 0}, + {0, 13, 0}, + {"pkcs-7-CertificateChoices", 1610612754, 0}, + {"certificate", 13, 0}, + {"pkcs-7-CertificateSet", 1610612751, 0}, + {0, 2, "pkcs-7-CertificateChoices"}, + {"pkcs-7-SignerInfos", 1610612751, 0}, + {0, 13, 0}, + {"pkcs-10-CertificationRequestInfo", 1610612741, 0}, + {"version", 1610874883, 0}, + {"v1", 1, "0"}, + {"subject", 1073741826, "Name"}, + {"subjectPKInfo", 1073741826, "SubjectPublicKeyInfo"}, + {"attributes", 536879106, "Attributes"}, + {0, 4104, "0"}, + {"Attributes", 1610612751, 0}, + {0, 2, "Attribute"}, + {"pkcs-10-CertificationRequest", 1610612741, 0}, + {"certificationRequestInfo", 1073741826, + "pkcs-10-CertificationRequestInfo"}, + {"signatureAlgorithm", 1073741826, "AlgorithmIdentifier"}, + {"signature", 6, 0}, + {"pkcs-9-ub-challengePassword", 1342177283, "255"}, + {"pkcs-9-certTypes", 1879048204, 0}, + {0, 1073741825, "pkcs-9"}, + {0, 1, "22"}, + {"pkcs-9-crlTypes", 1879048204, 0}, + {0, 1073741825, "pkcs-9"}, + {0, 1, "23"}, + {"pkcs-9-at-challengePassword", 1879048204, 0}, + {0, 1073741825, "pkcs-9"}, + {0, 1, "7"}, + {"pkcs-9-challengePassword", 1610612754, 0}, + {"printableString", 1612709890, "PrintableString"}, + {"pkcs-9-ub-challengePassword", 524298, "1"}, + {"utf8String", 538968066, "UTF8String"}, + {"pkcs-9-ub-challengePassword", 524298, "1"}, + {"pkcs-9-at-localKeyId", 1879048204, 0}, + {0, 1073741825, "pkcs-9"}, + {0, 1, "21"}, + {"pkcs-9-localKeyId", 1073741831, 0}, + {"pkcs-9-at-friendlyName", 1879048204, 0}, + {0, 1073741825, "pkcs-9"}, + {0, 1, "20"}, + {"pkcs-9-friendlyName", 1612709890, "BMPString"}, + {"255", 524298, "1"}, + {"pkcs-8-PrivateKeyInfo", 1610612741, 0}, + {"version", 1073741826, "pkcs-8-Version"}, + {"privateKeyAlgorithm", 1073741826, "AlgorithmIdentifier"}, + {"privateKey", 1073741826, "pkcs-8-PrivateKey"}, + {"attributes", 536895490, "Attributes"}, + {0, 4104, "0"}, + {"pkcs-8-Version", 1610874883, 0}, + {"v1", 1, "0"}, + {"pkcs-8-PrivateKey", 1073741831, 0}, + {"pkcs-8-Attributes", 1610612751, 0}, + {0, 2, "Attribute"}, + {"pkcs-8-EncryptedPrivateKeyInfo", 1610612741, 0}, + {"encryptionAlgorithm", 1073741826, "AlgorithmIdentifier"}, + {"encryptedData", 2, "pkcs-8-EncryptedData"}, + {"pkcs-8-EncryptedData", 1073741831, 0}, + {"pkcs-5", 1879048204, 0}, + {0, 1073741825, "pkcs"}, + {0, 1, "5"}, + {"pkcs-5-encryptionAlgorithm", 1879048204, 0}, + {"iso", 1073741825, "1"}, + {"member-body", 1073741825, "2"}, + {"us", 1073741825, "840"}, + {"rsadsi", 1073741825, "113549"}, + {0, 1, "3"}, + {"pkcs-5-des-EDE3-CBC", 1879048204, 0}, + {0, 1073741825, "pkcs-5-encryptionAlgorithm"}, + {0, 1, "7"}, + {"pkcs-5-des-EDE3-CBC-params", 1612709895, 0}, + {0, 1048586, "8"}, + {"pkcs-5-id-PBES2", 1879048204, 0}, + {0, 1073741825, "pkcs-5"}, + {0, 1, "13"}, + {"pkcs-5-PBES2-params", 1610612741, 0}, + {"keyDerivationFunc", 1073741826, "AlgorithmIdentifier"}, + {"encryptionScheme", 2, "AlgorithmIdentifier"}, + {"pkcs-5-id-PBKDF2", 1879048204, 0}, + {0, 1073741825, "pkcs-5"}, + {0, 1, "12"}, + {"pkcs-5-PBKDF2-params", 1610612741, 0}, + {"salt", 1610612754, 0}, + {"specified", 1073741831, 0}, + {"otherSource", 2, "AlgorithmIdentifier"}, + {"iterationCount", 1611137027, 0}, + {"1", 10, "MAX"}, + {"keyLength", 1611153411, 0}, + {"1", 10, "MAX"}, + {"prf", 16386, "AlgorithmIdentifier"}, + {"pkcs-12", 1879048204, 0}, + {0, 1073741825, "pkcs"}, + {0, 1, "12"}, + {"pkcs-12-PFX", 1610612741, 0}, + {"version", 1610874883, 0}, + {"v3", 1, "3"}, + {"authSafe", 1073741826, "pkcs-7-ContentInfo"}, + {"macData", 16386, "pkcs-12-MacData"}, + {"pkcs-12-PbeParams", 1610612741, 0}, + {"salt", 1073741831, 0}, + {"iterations", 3, 0}, + {"pkcs-12-MacData", 1610612741, 0}, + {"mac", 1073741826, "pkcs-7-DigestInfo"}, + {"macSalt", 1073741831, 0}, + {"iterations", 536903683, 0}, + {0, 9, "1"}, + {"pkcs-12-AuthenticatedSafe", 1610612747, 0}, + {0, 2, "pkcs-7-ContentInfo"}, + {"pkcs-12-SafeContents", 1610612747, 0}, + {0, 2, "pkcs-12-SafeBag"}, + {"pkcs-12-SafeBag", 1610612741, 0}, + {"bagId", 1073741836, 0}, + {"bagValue", 1614815245, 0}, + {0, 1073743880, "0"}, + {"badId", 1, 0}, + {"bagAttributes", 536887311, 0}, + {0, 2, "pkcs-12-PKCS12Attribute"}, + {"pkcs-12-bagtypes", 1879048204, 0}, + {0, 1073741825, "pkcs-12"}, + {0, 1073741825, "10"}, + {0, 1, "1"}, + {"pkcs-12-keyBag", 1879048204, 0}, + {0, 1073741825, "pkcs-12-bagtypes"}, + {0, 1, "1"}, + {"pkcs-12-pkcs8ShroudedKeyBag", 1879048204, 0}, + {0, 1073741825, "pkcs-12-bagtypes"}, + {0, 1, "2"}, + {"pkcs-12-certBag", 1879048204, 0}, + {0, 1073741825, "pkcs-12-bagtypes"}, + {0, 1, "3"}, + {"pkcs-12-crlBag", 1879048204, 0}, + {0, 1073741825, "pkcs-12-bagtypes"}, + {0, 1, "4"}, + {"pkcs-12-KeyBag", 1073741826, "pkcs-8-PrivateKeyInfo"}, + {"pkcs-12-PKCS8ShroudedKeyBag", 1073741826, + "pkcs-8-EncryptedPrivateKeyInfo"}, + {"pkcs-12-CertBag", 1610612741, 0}, + {"certId", 1073741836, 0}, + {"certValue", 541073421, 0}, + {0, 1073743880, "0"}, + {"certId", 1, 0}, + {"pkcs-12-CRLBag", 1610612741, 0}, + {"crlId", 1073741836, 0}, + {"crlValue", 541073421, 0}, + {0, 1073743880, "0"}, + {"crlId", 1, 0}, + {"pkcs-12-PKCS12Attribute", 1073741826, "Attribute"}, + {"pkcs-7-data", 1879048204, 0}, + {"iso", 1073741825, "1"}, + {"member-body", 1073741825, "2"}, + {"us", 1073741825, "840"}, + {"rsadsi", 1073741825, "113549"}, + {"pkcs", 1073741825, "1"}, + {"pkcs7", 1073741825, "7"}, + {0, 1, "1"}, + {"pkcs-7-encryptedData", 1879048204, 0}, + {"iso", 1073741825, "1"}, + {"member-body", 1073741825, "2"}, + {"us", 1073741825, "840"}, + {"rsadsi", 1073741825, "113549"}, + {"pkcs", 1073741825, "1"}, + {"pkcs7", 1073741825, "7"}, + {0, 1, "6"}, + {"pkcs-7-Data", 1073741831, 0}, + {"pkcs-7-EncryptedData", 1610612741, 0}, + {"version", 1073741826, "pkcs-7-CMSVersion"}, + {"encryptedContentInfo", 1073741826, "pkcs-7-EncryptedContentInfo"}, + {"unprotectedAttrs", 536895490, "pkcs-7-UnprotectedAttributes"}, + {0, 4104, "1"}, + {"pkcs-7-EncryptedContentInfo", 1610612741, 0}, + {"contentType", 1073741826, "pkcs-7-ContentType"}, + {"contentEncryptionAlgorithm", 1073741826, + "pkcs-7-ContentEncryptionAlgorithmIdentifier"}, + {"encryptedContent", 536895490, "pkcs-7-EncryptedContent"}, + {0, 4104, "0"}, + {"pkcs-7-ContentEncryptionAlgorithmIdentifier", 1073741826, + "AlgorithmIdentifier"}, + {"pkcs-7-EncryptedContent", 1073741831, 0}, + {"pkcs-7-UnprotectedAttributes", 1612709903, 0}, + {"MAX", 1074266122, "1"}, + {0, 2, "Attribute"}, + {"id-at-ldap-DC", 1880096780, "AttributeType"}, + {0, 1073741825, "0"}, + {0, 1073741825, "9"}, + {0, 1073741825, "2342"}, + {0, 1073741825, "19200300"}, + {0, 1073741825, "100"}, + {0, 1073741825, "1"}, + {0, 1, "25"}, + {"ldap-DC", 1073741826, "IA5String"}, + {"id-at-ldap-UID", 1880096780, "AttributeType"}, + {0, 1073741825, "0"}, + {0, 1073741825, "9"}, + {0, 1073741825, "2342"}, + {0, 1073741825, "19200300"}, + {0, 1073741825, "100"}, + {0, 1073741825, "1"}, + {0, 1, "1"}, + {"ldap-UID", 1073741826, "DirectoryString"}, + {"id-pda", 1879048204, 0}, + {0, 1073741825, "id-pkix"}, + {0, 1, "9"}, + {"id-pda-dateOfBirth", 1880096780, "AttributeType"}, + {0, 1073741825, "id-pda"}, + {0, 1, "1"}, + {"DateOfBirth", 1082130449, 0}, + {"id-pda-placeOfBirth", 1880096780, "AttributeType"}, + {0, 1073741825, "id-pda"}, + {0, 1, "2"}, + {"PlaceOfBirth", 1073741826, "DirectoryString"}, + {"id-pda-gender", 1880096780, "AttributeType"}, + {0, 1073741825, "id-pda"}, + {0, 1, "3"}, + {"Gender", 1612709890, "PrintableString"}, + {0, 1048586, "1"}, + {"id-pda-countryOfCitizenship", 1880096780, "AttributeType"}, + {0, 1073741825, "id-pda"}, + {0, 1, "4"}, + {"CountryOfCitizenship", 1612709890, "PrintableString"}, + {0, 1048586, "2"}, + {"id-pda-countryOfResidence", 1880096780, "AttributeType"}, + {0, 1073741825, "id-pda"}, + {0, 1, "5"}, + {"CountryOfResidence", 1612709890, "PrintableString"}, + {0, 1048586, "2"}, + {"id-pe-proxyCertInfo", 1879048204, 0}, + {0, 1073741825, "id-pe"}, + {0, 1, "14"}, + {"id-ppl-inheritAll", 1879048204, 0}, + {0, 1073741825, "id-pkix"}, + {0, 1073741825, "21"}, + {0, 1, "1"}, + {"id-ppl-independent", 1879048204, 0}, + {0, 1073741825, "id-pkix"}, + {0, 1073741825, "21"}, + {0, 1, "2"}, + {"ProxyCertInfo", 1610612741, 0}, + {"pCPathLenConstraint", 1611153411, 0}, + {"0", 10, "MAX"}, + {"proxyPolicy", 2, "ProxyPolicy"}, + {"ProxyPolicy", 1610612741, 0}, + {"policyLanguage", 1073741836, 0}, + {"policy", 16391, 0}, + {"id-on", 1879048204, 0}, + {0, 1073741825, "id-pkix"}, + {0, 1, "8"}, + {"id-on-xmppAddr", 1879048204, 0}, + {0, 1073741825, "id-on"}, + {0, 1, "5"}, + {"XmppAddr", 2, "UTF8String"}, + {0, 0, 0} +}; diff --git a/src/daemon/https/tls/x509_b64.c b/src/daemon/https/tls/x509_b64.c new file mode 100644 index 00000000..50a9b475 --- /dev/null +++ b/src/daemon/https/tls/x509_b64.c @@ -0,0 +1,599 @@ +/* + * Copyright (C) 2000, 2001, 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +/* Functions that relate to base64 encoding and decoding. + */ + +#include "gnutls_int.h" +#include "gnutls_errors.h" +#include +#include + +static const uint8_t b64table[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +static const uint8_t asciitable[128] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f, + 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x3a, 0x3b, 0x3c, 0x3d, 0xff, 0xff, + 0xff, 0xf1, 0xff, 0xff, 0xff, 0x00, /* 0xf1 for '=' */ + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, + 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, + 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, + 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x19, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, + 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, + 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, + 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, + 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, + 0xff, 0xff +}; + +inline static int +encode (char *result, const uint8_t * data, int left) +{ + + int data_len; + + if (left > 3) + data_len = 3; + else + data_len = left; + + switch (data_len) + { + case 3: + result[0] = b64table[(data[0] >> 2)]; + result[1] = + b64table[(((((data[0] & 0x03) & 0xff) << 4) & 0xff) | + (data[1] >> 4))]; + result[2] = + b64table[((((data[1] & 0x0f) << 2) & 0xff) | (data[2] >> 6))]; + result[3] = b64table[(((data[2] << 2) & 0xff) >> 2)]; + break; + case 2: + result[0] = b64table[(data[0] >> 2)]; + result[1] = + b64table[(((((data[0] & 0x03) & 0xff) << 4) & 0xff) | + (data[1] >> 4))]; + result[2] = b64table[(((data[1] << 4) & 0xff) >> 2)]; + result[3] = '='; + break; + case 1: + result[0] = b64table[(data[0] >> 2)]; + result[1] = b64table[(((((data[0] & 0x03) & 0xff) << 4) & 0xff))]; + result[2] = '='; + result[3] = '='; + break; + default: + return -1; + } + + return 4; + +} + +/* data must be 4 bytes + * result should be 3 bytes + */ +#define TOASCII(c) (c < 127 ? asciitable[c] : 0xff) +inline static int +decode (uint8_t * result, const opaque * data) +{ + uint8_t a1, a2; + int ret = 3; + + a1 = TOASCII (data[0]); + a2 = TOASCII (data[1]); + if (a1 == 0xff || a2 == 0xff) + return -1; + result[0] = ((a1 << 2) & 0xff) | ((a2 >> 4) & 0xff); + + a1 = a2; + a2 = TOASCII (data[2]); + if (a2 == 0xff) + return -1; + result[1] = ((a1 << 4) & 0xff) | ((a2 >> 2) & 0xff); + + a1 = a2; + a2 = TOASCII (data[3]); + if (a2 == 0xff) + return -1; + result[2] = ((a1 << 6) & 0xff) | (a2 & 0xff); + + if (data[2] == '=') + ret--; + + if (data[3] == '=') + ret--; + return ret; +} + +/* encodes data and puts the result into result (locally allocated) + * The result_size is the return value + */ +int +_gnutls_base64_encode (const uint8_t * data, size_t data_size, + uint8_t ** result) +{ + unsigned int i, j; + int ret, tmp; + char tmpres[4]; + + ret = B64SIZE (data_size); + + (*result) = gnutls_malloc (ret + 1); + if ((*result) == NULL) + return GNUTLS_E_MEMORY_ERROR; + + for (i = j = 0; i < data_size; i += 3, j += 4) + { + tmp = encode (tmpres, &data[i], data_size - i); + if (tmp == -1) + { + gnutls_free ((*result)); + return GNUTLS_E_MEMORY_ERROR; + } + memcpy (&(*result)[j], tmpres, tmp); + } + (*result)[ret] = 0; /* null terminated */ + + return ret; +} + +#define INCR(what, size) \ + do { \ + what+=size; \ + if (what > ret) { \ + gnutls_assert(); \ + gnutls_free( (*result)); *result = NULL; \ + return GNUTLS_E_INTERNAL_ERROR; \ + } \ + } while(0) + +/* encodes data and puts the result into result (locally allocated) + * The result_size (including the null terminator) is the return value. + */ +int +_gnutls_fbase64_encode (const char *msg, const uint8_t * data, + int data_size, uint8_t ** result) +{ + int i, ret, tmp, j; + char tmpres[4]; + uint8_t *ptr; + uint8_t top[80]; + uint8_t bottom[80]; + int pos, bytes, top_len, bottom_len; + size_t msglen = strlen (msg); + + if (msglen > 50) + { + gnutls_assert (); + return GNUTLS_E_BASE64_ENCODING_ERROR; + } + + memset (bottom, 0, sizeof (bottom)); + memset (top, 0, sizeof (top)); + + strcat (top, "-----BEGIN "); /* Flawfinder: ignore */ + strcat (top, msg); /* Flawfinder: ignore */ + strcat (top, "-----"); /* Flawfinder: ignore */ + + strcat (bottom, "\n-----END "); /* Flawfinder: ignore */ + strcat (bottom, msg); /* Flawfinder: ignore */ + strcat (bottom, "-----\n"); /* Flawfinder: ignore */ + + top_len = strlen (top); + bottom_len = strlen (bottom); + + ret = B64FSIZE (msglen, data_size); + + (*result) = gnutls_calloc (1, ret + 1); + if ((*result) == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + bytes = pos = 0; + INCR (bytes, top_len); + pos = top_len; + + strcpy (*result, top); /* Flawfinder: ignore */ + + for (i = j = 0; i < data_size; i += 3, j += 4) + { + + tmp = encode (tmpres, &data[i], data_size - i); + if (tmp == -1) + { + gnutls_assert (); + gnutls_free ((*result)); + *result = NULL; + return GNUTLS_E_BASE64_ENCODING_ERROR; + } + + INCR (bytes, 4); + ptr = &(*result)[j + pos]; + + if ((j) % 64 == 0) + { + INCR (bytes, 1); + pos++; + *ptr++ = '\n'; + } + *ptr++ = tmpres[0]; + + if ((j + 1) % 64 == 0) + { + INCR (bytes, 1); + pos++; + *ptr++ = '\n'; + } + *ptr++ = tmpres[1]; + + if ((j + 2) % 64 == 0) + { + INCR (bytes, 1); + pos++; + *ptr++ = '\n'; + } + *ptr++ = tmpres[2]; + + if ((j + 3) % 64 == 0) + { + INCR (bytes, 1); + pos++; + *ptr++ = '\n'; + } + *ptr++ = tmpres[3]; + } + + INCR (bytes, bottom_len); + + memcpy (&(*result)[bytes - bottom_len], bottom, bottom_len); + (*result)[bytes] = 0; + + return ret + 1; +} + +/** + * gnutls_pem_base64_encode - This function will convert raw data to Base64 encoded + * @msg: is a message to be put in the header + * @data: contain the raw data + * @result: the place where base64 data will be copied + * @result_size: holds the size of the result + * + * This function will convert the given data to printable data, using the base64 + * encoding. This is the encoding used in PEM messages. If the provided + * buffer is not long enough GNUTLS_E_SHORT_MEMORY_BUFFER is returned. + * + * The output string will be null terminated, although the size will not include + * the terminating null. + * + **/ +int +gnutls_pem_base64_encode (const char *msg, const gnutls_datum_t * data, + char *result, size_t * result_size) +{ + opaque *ret; + int size; + + size = _gnutls_fbase64_encode (msg, data->data, data->size, &ret); + if (size < 0) + return size; + + if (result == NULL || *result_size < (unsigned) size) + { + gnutls_free (ret); + *result_size = size; + return GNUTLS_E_SHORT_MEMORY_BUFFER; + } + else + { + memcpy (result, ret, size); + gnutls_free (ret); + *result_size = size - 1; + } + + return 0; +} + +/** + * gnutls_pem_base64_encode_alloc - This function will convert raw data to Base64 encoded + * @msg: is a message to be put in the encoded header + * @data: contains the raw data + * @result: will hold the newly allocated encoded data + * + * This function will convert the given data to printable data, using the base64 + * encoding. This is the encoding used in PEM messages. This function will + * allocate the required memory to hold the encoded data. + * + * You should use gnutls_free() to free the returned data. + * + **/ +int +gnutls_pem_base64_encode_alloc (const char *msg, + const gnutls_datum_t * data, + gnutls_datum_t * result) +{ + opaque *ret; + int size; + + if (result == NULL) + return GNUTLS_E_INVALID_REQUEST; + + size = _gnutls_fbase64_encode (msg, data->data, data->size, &ret); + if (size < 0) + return size; + + result->data = ret; + result->size = size - 1; + return 0; +} + + +/* decodes data and puts the result into result (locally allocated) + * The result_size is the return value + */ +int +_gnutls_base64_decode (const uint8_t * data, size_t data_size, + uint8_t ** result) +{ + unsigned int i, j; + int ret, tmp, est; + uint8_t tmpres[3]; + + est = ((data_size * 3) / 4) + 1; + (*result) = gnutls_malloc (est); + if ((*result) == NULL) + return GNUTLS_E_MEMORY_ERROR; + + ret = 0; + for (i = j = 0; i < data_size; i += 4, j += 3) + { + tmp = decode (tmpres, &data[i]); + if (tmp < 0) + { + gnutls_free (*result); + *result = NULL; + return tmp; + } + memcpy (&(*result)[j], tmpres, tmp); + ret += tmp; + } + return ret; +} + +/* copies data to result but removes newlines and + * returns the size of the data copied. + */ +inline static int +cpydata (const uint8_t * data, int data_size, uint8_t ** result) +{ + int i, j; + + (*result) = gnutls_malloc (data_size); + if (*result == NULL) + return GNUTLS_E_MEMORY_ERROR; + + for (j = i = 0; i < data_size; i++) + { + if (data[i] == '\n' || data[i] == '\r') + continue; + (*result)[j] = data[i]; + j++; + } + return j; +} + +/* Searches the given string for ONE PEM encoded certificate, and + * stores it in the result. + * + * The result_size is the return value + */ +#define ENDSTR "-----\n" +#define ENDSTR2 "-----\r" +int +_gnutls_fbase64_decode (const char *header, const opaque * data, + size_t data_size, uint8_t ** result) +{ + int ret; + static const char top[] = "-----BEGIN "; + static const char bottom[] = "\n-----END "; + uint8_t *rdata; + int rdata_size; + uint8_t *kdata; + int kdata_size; + char pem_header[128]; + + _gnutls_str_cpy (pem_header, sizeof (pem_header), top); + if (header != NULL) + _gnutls_str_cat (pem_header, sizeof (pem_header), header); + + rdata = memmem (data, data_size, pem_header, strlen (pem_header)); + + if (rdata == NULL) + { + gnutls_assert (); + _gnutls_debug_log ("Could not find '%s'\n", pem_header); + return GNUTLS_E_BASE64_UNEXPECTED_HEADER_ERROR; + } + + data_size -= (unsigned long int) rdata - (unsigned long int) data; + + if (data_size < 4 + strlen (bottom)) + { + gnutls_assert (); + return GNUTLS_E_BASE64_DECODING_ERROR; + } + + kdata = memmem (rdata, data_size, ENDSTR, sizeof (ENDSTR) - 1); + /* allow CR as well. + */ + if (kdata == NULL) + kdata = memmem (rdata, data_size, ENDSTR2, sizeof (ENDSTR2) - 1); + + if (kdata == NULL) + { + gnutls_assert (); + _gnutls_x509_log ("Could not find '%s'\n", ENDSTR); + return GNUTLS_E_BASE64_DECODING_ERROR; + } + data_size -= strlen (ENDSTR); + data_size -= (unsigned long int) kdata - (unsigned long int) rdata; + + rdata = kdata + strlen (ENDSTR); + + /* position is now after the ---BEGIN--- headers */ + + kdata = memmem (rdata, data_size, bottom, strlen (bottom)); + if (kdata == NULL) + { + gnutls_assert (); + return GNUTLS_E_BASE64_DECODING_ERROR; + } + + /* position of kdata is before the ----END--- footer + */ + rdata_size = (unsigned long int) kdata - (unsigned long int) rdata; + + if (rdata_size < 4) + { + gnutls_assert (); + return GNUTLS_E_BASE64_DECODING_ERROR; + } + + kdata_size = cpydata (rdata, rdata_size, &kdata); + + if (kdata_size < 0) + { + gnutls_assert (); + return kdata_size; + } + + if (kdata_size < 4) + { + gnutls_assert (); + gnutls_free (kdata); + return GNUTLS_E_BASE64_DECODING_ERROR; + } + + if ((ret = _gnutls_base64_decode (kdata, kdata_size, result)) < 0) + { + gnutls_free (kdata); + gnutls_assert (); + return GNUTLS_E_BASE64_DECODING_ERROR; + } + gnutls_free (kdata); + + return ret; +} + +/** + * gnutls_pem_base64_decode - This function will decode base64 encoded data + * @header: A null terminated string with the PEM header (eg. CERTIFICATE) + * @b64_data: contain the encoded data + * @result: the place where decoded data will be copied + * @result_size: holds the size of the result + * + * This function will decode the given encoded data. If the header given + * is non null this function will search for "-----BEGIN header" and decode + * only this part. Otherwise it will decode the first PEM packet found. + * + * Returns GNUTLS_E_SHORT_MEMORY_BUFFER if the buffer given is not long enough, + * or 0 on success. + **/ +int +gnutls_pem_base64_decode (const char *header, + const gnutls_datum_t * b64_data, + unsigned char *result, size_t * result_size) +{ + opaque *ret; + int size; + + size = + _gnutls_fbase64_decode (header, b64_data->data, b64_data->size, &ret); + if (size < 0) + return size; + + if (result == NULL || *result_size < (unsigned) size) + { + gnutls_free (ret); + *result_size = size; + return GNUTLS_E_SHORT_MEMORY_BUFFER; + } + else + { + memcpy (result, ret, size); + gnutls_free (ret); + *result_size = size; + } + + return 0; +} + +/** + * gnutls_pem_base64_decode_alloc - This function will decode base64 encoded data + * @header: The PEM header (eg. CERTIFICATE) + * @b64_data: contains the encoded data + * @result: the place where decoded data lie + * + * This function will decode the given encoded data. The decoded data + * will be allocated, and stored into result. + * If the header given is non null this function will search for + * "-----BEGIN header" and decode only this part. Otherwise it will decode the + * first PEM packet found. + * + * You should use gnutls_free() to free the returned data. + * + **/ +int +gnutls_pem_base64_decode_alloc (const char *header, + const gnutls_datum_t * b64_data, + gnutls_datum_t * result) +{ + opaque *ret; + int size; + + if (result == NULL) + return GNUTLS_E_INVALID_REQUEST; + + size = + _gnutls_fbase64_decode (header, b64_data->data, b64_data->size, &ret); + if (size < 0) + return size; + + result->data = ret; + result->size = size; + return 0; +} diff --git a/src/daemon/https/tls/x509_b64.h b/src/daemon/https/tls/x509_b64.h new file mode 100644 index 00000000..539bec42 --- /dev/null +++ b/src/daemon/https/tls/x509_b64.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +int _gnutls_base64_encode (const uint8_t * data, size_t data_size, + uint8_t ** result); +int _gnutls_fbase64_encode (const char *msg, const uint8_t * data, + int data_size, uint8_t ** result); +int _gnutls_base64_decode (const uint8_t * data, size_t data_size, + uint8_t ** result); +int _gnutls_fbase64_decode (const char *header, const uint8_t * data, + size_t data_size, uint8_t ** result); + +#define B64SIZE( data_size) ((data_size%3==0)?((data_size*4)/3):(4+((data_size/3)*4))) + +/* The size for B64 encoding + newlines plus header + */ + +#define HEADSIZE( hsize) \ + sizeof("-----BEGIN ")-1+sizeof("-----")-1+ \ + sizeof("\n-----END ")-1+sizeof("-----\n")-1+hsize+hsize + +#define B64FSIZE( hsize, dsize) \ + (B64SIZE(dsize) + HEADSIZE(hsize) + /*newlines*/ \ + B64SIZE(dsize)/64 + (((B64SIZE(dsize) % 64) > 0) ? 1 : 0)) diff --git a/src/daemon/https/tls_test.c b/src/daemon/https/tls_test.c new file mode 100644 index 00000000..492521e8 --- /dev/null +++ b/src/daemon/https/tls_test.c @@ -0,0 +1,348 @@ +/* + * Copyright (C) 2000,2001,2002,2003,2006,2007 Nikos Mavrogiannopoulos + * Copyright (C) 2004,2005 Free Software Foundation + * + * This file is part of GNUTLS. + * + * GNUTLS is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GNUTLS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef SHUT_WR +# define SHUT_WR 1 +#endif + +#ifndef SHUT_RDWR +# define SHUT_RDWR 2 +#endif + +#define SA struct sockaddr +#define ERR(err,s) if (err==-1) {perror(s);return(1);} +#define MAX_BUF 4096 + +/* global stuff here */ +int resume; +char *hostname = NULL; +int port; +int record_max_size; +int fingerprint; +static int debug; + +gnutls_srp_client_credentials_t srp_cred; +gnutls_anon_client_credentials_t anon_cred; +gnutls_certificate_credentials_t xcred; + +/* end of global stuff */ + + +int verbose = 0; + +extern int tls1_ok; +extern int tls1_1_ok; +extern int ssl3_ok; + +static void +tls_log_func (int level, const char *str) +{ + fprintf (stderr, "|<%d>| %s", level, str); +} + +typedef test_code_t (*TEST_FUNC) (gnutls_session_t); + +typedef struct +{ + char *test_name; + TEST_FUNC func; + char *suc_str; + char *fail_str; + char *unsure_str; +} TLS_TEST; + +static const TLS_TEST tls_tests[] = { + {"for TLS 1.1 support", test_tls1_1, "yes", "no", "dunno"}, + {"fallback from TLS 1.1 to", test_tls1_1_fallback, "TLS 1.0", "failed", + "SSL 3.0"}, + {"for TLS 1.0 support", test_tls1, "yes", "no", "dunno"}, + {"for SSL 3.0 support", test_ssl3, "yes", "no", "dunno"}, + {"for HTTPS server name", test_server, "", "failed", "not checked"}, + {"for version rollback bug in RSA PMS", test_rsa_pms, "no", "yes", + "dunno"}, + {"for version rollback bug in Client Hello", test_version_rollback, + "no", "yes", "dunno"}, + + /* this test will disable TLS 1.0 if the server is + * buggy */ + {"whether we need to disable TLS 1.0", test_tls_disable, "no", "yes", + "dunno"}, + + {"whether the server ignores the RSA PMS version", + test_rsa_pms_version_check, "yes", "no", "dunno"}, + {"whether the server can accept Hello Extensions", + test_hello_extension, "yes", "no", "dunno"}, + {"whether the server can accept cipher suites not in SSL 3.0 spec", + test_unknown_ciphersuites, "yes", "no", "dunno"}, + {"whether the server can accept a bogus TLS record version in the client hello", test_version_oob, "yes", "no", "dunno"}, + {"for certificate information", test_certificate, "", "", ""}, + {"for trusted CAs", test_server_cas, "", "", ""}, + {"whether the server understands TLS closure alerts", test_bye, "yes", + "no", "partially"}, + /* the fact that is after the closure alert test does matter. + */ + {"whether the server supports session resumption", + test_session_resume2, "yes", "no", "dunno"}, + {"for export-grade ciphersuite support", test_export, "yes", "no", + "dunno"}, + {"RSA-export ciphersuite info", test_export_info, "", "N/A", "N/A"}, +#ifdef ENABLE_ANON + {"for anonymous authentication support", test_anonymous, "yes", "no", + "dunno"}, + {"anonymous Diffie Hellman group info", test_dhe_group, "", "N/A", + "N/A"}, +#endif + {"for ephemeral Diffie Hellman support", test_dhe, "yes", "no", + "dunno"}, + {"ephemeral Diffie Hellman group info", test_dhe_group, "", "N/A", + "N/A"}, + {"for AES cipher support (TLS extension)", test_aes, "yes", "no", + "dunno"}, +#ifdef ENABLE_CAMELLIA + {"for CAMELLIA cipher support (TLS extension)", test_camellia, "yes", "no", + "dunno"}, +#endif + {"for 3DES cipher support", test_3des, "yes", "no", "dunno"}, + {"for ARCFOUR 128 cipher support", test_arcfour, "yes", "no", "dunno"}, + {"for ARCFOUR 40 cipher support", test_arcfour_40, "yes", "no", + "dunno"}, + {"for MD5 MAC support", test_md5, "yes", "no", "dunno"}, + {"for SHA1 MAC support", test_sha, "yes", "no", "dunno"}, +#ifdef HAVE_LIBZ + {"for ZLIB compression support (TLS extension)", test_zlib, "yes", + "no", "dunno"}, +#endif + {"for LZO compression support (GnuTLS extension)", test_lzo, "yes", + "no", "dunno"}, + {"for max record size (TLS extension)", test_max_record_size, "yes", + "no", "dunno"}, +#ifdef ENABLE_SRP + {"for SRP authentication support (TLS extension)", test_srp, "yes", + "no", "dunno"}, +#endif + {"for OpenPGP authentication support (TLS extension)", test_openpgp1, + "yes", "no", "dunno"}, + {NULL, NULL, NULL, NULL, NULL} +}; + +static int tt = 0; +const char *ip; + +static void gaa_parser (int argc, char **argv); + +int +main (int argc, char **argv) +{ + int err, ret; + int sd, i; + gnutls_session_t state; + char buffer[MAX_BUF + 1]; + char portname[6]; + struct addrinfo hints, *res, *ptr; + + gaa_parser (argc, argv); + +#ifndef _WIN32 + signal (SIGPIPE, SIG_IGN); +#endif + + sockets_init (); + + if (gnutls_global_init () < 0) + { + fprintf (stderr, "global state initialization error\n"); + exit (1); + } + + gnutls_global_set_log_function (tls_log_func); + gnutls_global_set_log_level (debug); + + if (gnutls_global_init_extra () < 0) + { + fprintf (stderr, "global state initialization error\n"); + exit (1); + } + + printf ("Resolving '%s'...\n", hostname); + /* get server name */ + memset (&hints, 0, sizeof (hints)); + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = 0; + snprintf (portname, sizeof (portname), "%d", port); + if ((err = getaddrinfo (hostname, portname, &hints, &res)) != 0) + { + fprintf (stderr, "Cannot resolve %s: %s\n", hostname, + gai_strerror (err)); + exit (1); + } + + /* X509 stuff */ + if (gnutls_certificate_allocate_credentials (&xcred) < 0) + { /* space for 2 certificates */ + fprintf (stderr, "memory error\n"); + exit (1); + } + + /* SRP stuff */ +#ifdef ENABLE_SRP + if (gnutls_srp_allocate_client_credentials (&srp_cred) < 0) + { + fprintf (stderr, "memory error\n"); + exit (1); + } +#endif + +#ifdef ENABLE_ANON + /* ANON stuff */ + if (gnutls_anon_allocate_client_credentials (&anon_cred) < 0) + { + fprintf (stderr, "memory error\n"); + exit (1); + } +#endif + + i = 0; + + do + { + + if (tls_tests[i].test_name == NULL) + break; /* finished */ + + /* if neither of SSL3 and TLSv1 are supported, exit + */ + if (i > 3 && tls1_1_ok == 0 && tls1_ok == 0 && ssl3_ok == 0) + { + fprintf (stderr, + "\nServer does not support any of SSL 3.0, TLS 1.0 and TLS 1.1\n"); + break; + } + + sd = -1; + for (ptr = res; ptr != NULL; ptr = ptr->ai_next) + { + sd = socket (ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol); + if (sd == -1) + { + continue; + } + + getnameinfo (ptr->ai_addr, ptr->ai_addrlen, buffer, MAX_BUF, + NULL, 0, NI_NUMERICHOST); + if (tt++ == 0) + printf ("Connecting to '%s:%d'...\n", buffer, port); + if ((err = connect (sd, ptr->ai_addr, ptr->ai_addrlen)) != 0) + { + close (sd); + sd = -1; + continue; + } + } + ERR (err, "connect") gnutls_init (&state, GNUTLS_CLIENT); + gnutls_transport_set_ptr (state, (gnutls_transport_ptr_t) sd); + + do + { + printf ("Checking %s...", tls_tests[i].test_name); + + ret = tls_tests[i].func (state); + + if (ret == TEST_SUCCEED) + printf (" %s\n", tls_tests[i].suc_str); + else if (ret == TEST_FAILED) + printf (" %s\n", tls_tests[i].fail_str); + else if (ret == TEST_UNSURE) + printf (" %s\n", tls_tests[i].unsure_str); + else if (ret == TEST_IGNORE) + { + printf (" N/A\n"); + i++; + } + } + while (ret == TEST_IGNORE && tls_tests[i].test_name != NULL); + + gnutls_deinit (state); + + shutdown (sd, SHUT_RDWR); /* no more receptions */ + close (sd); + + i++; + } + while (1); + + freeaddrinfo (res); + +#ifdef ENABLE_SRP + gnutls_srp_free_client_credentials (srp_cred); +#endif + gnutls_certificate_free_credentials (xcred); +#ifdef ENABLE_ANON + gnutls_anon_free_client_credentials (anon_cred); +#endif + gnutls_global_deinit (); + + return 0; +} + +static gaainfo info; +void +gaa_parser (int argc, char **argv) +{ + if (gaa (argc, argv, &info) != -1) + { + fprintf (stderr, + "Error in the arguments. Use the -h or --help parameters to get more info.\n"); + exit (1); + } + + port = info.pp; + if (info.rest_args == NULL) + hostname = "localhost"; + else + hostname = info.rest_args; + + debug = info.debug; + + verbose = info.more_info; + +} + +void +tls_test_version (void) +{ + const char *v = gnutls_check_version (NULL); + + printf ("gnutls-cli-debug (GnuTLS) %s\n", LIBGNUTLS_VERSION); + if (strcmp (v, LIBGNUTLS_VERSION) != 0) + printf ("libgnutls %s\n", v); +} diff --git a/src/daemon/https/x509/Makefile.am b/src/daemon/https/x509/Makefile.am new file mode 100644 index 00000000..c3a30f03 --- /dev/null +++ b/src/daemon/https/x509/Makefile.am @@ -0,0 +1,36 @@ + +AM_CPPFLAGS = -I./includes \ +-I$(top_srcdir)/src/daemon/https/includes \ +-I$(top_srcdir)/src/daemon/https/minitasn1 \ +-I$(top_srcdir)/src/daemon/https/lgl \ +-I$(top_srcdir)/src/daemon/https/x509 \ +-I$(top_srcdir)/src/daemon/https/tls + +noinst_LTLIBRARIES = libx509.la + +libx509_la_LDFLAGS = -lgcrypt +# -l $(top_srcdir)/src/daemon/https/lgl/liblgl.la + +libx509_la_SOURCES = \ +common.c \ +crq.c \ +crl.c \ +crl_write.c \ +dn.c \ +dsa.c \ +extensions.c \ +mpi.c \ +pkcs12_bag.c \ +pkcs12.c \ +pkcs12_encr.c \ +pkcs7.c \ +x509_privkey.c \ +privkey_pkcs8.c \ +rfc2818_hostname.c \ +sign.c \ +x509_verify.c \ +x509.c \ +x509_write.c +# output.c + + diff --git a/src/daemon/https/x509/common.c b/src/daemon/https/x509/common.c new file mode 100644 index 00000000..98a655c7 --- /dev/null +++ b/src/daemon/https/x509/common.c @@ -0,0 +1,1491 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2006, 2007 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct _oid2string +{ + const char *oid; + const char *ldap_desc; + int choice; /* of type DirectoryString */ + int printable; +} oid2string; + +/* This list contains all the OIDs that may be + * contained in a rdnSequence and are printable. + */ +static const oid2string _oid2str[] = { + /* PKIX + */ + {"1.3.6.1.5.5.7.9.1", + "dateOfBirth", + 0, + 1}, + {"1.3.6.1.5.5.7.9.2", + "placeOfBirth", + 0, + 1}, + {"1.3.6.1.5.5.7.9.3", + "gender", + 0, + 1}, + {"1.3.6.1.5.5.7.9.4", + "countryOfCitizenship", + 0, + 1}, + {"1.3.6.1.5.5.7.9.5", + "countryOfResidence", + 0, + 1}, + + {"2.5.4.6", + "C", + 0, + 1}, + {"2.5.4.9", + "STREET", + 1, + 1}, + {"2.5.4.12", + "T", + 1, + 1}, + {"2.5.4.10", + "O", + 1, + 1}, + {"2.5.4.11", + "OU", + 1, + 1}, + {"2.5.4.3", + "CN", + 1, + 1}, + {"2.5.4.7", + "L", + 1, + 1}, + {"2.5.4.8", + "ST", + 1, + 1}, + + {"2.5.4.5", + "serialNumber", + 0, + 1}, + {"2.5.4.20", + "telephoneNumber", + 0, + 1}, + {"2.5.4.4", + "surName", + 1, + 1}, + {"2.5.4.43", + "initials", + 1, + 1}, + {"2.5.4.44", + "generationQualifier", + 1, + 1}, + {"2.5.4.42", + "givenName", + 1, + 1}, + {"2.5.4.65", + "pseudonym", + 1, + 1}, + {"2.5.4.46", + "dnQualifier", + 0, + 1}, + + {"0.9.2342.19200300.100.1.25", + "DC", + 0, + 1}, + {"0.9.2342.19200300.100.1.1", + "UID", + 1, + 1}, + + /* PKCS #9 + */ + {"1.2.840.113549.1.9.1", + "EMAIL", + 0, + 1}, + {"1.2.840.113549.1.9.7", + NULL, + 1, + 1}, + + /* friendly name */ + {"1.2.840.113549.1.9.20", + NULL, + 0, + 1}, + {NULL, + NULL, + 0, + 0} +}; + +/* Returns 1 if the data defined by the OID are printable. + */ +int +_gnutls_x509_oid_data_printable (const char *oid) +{ + int i = 0; + + do + { + if (strcmp (_oid2str[i].oid, oid) == 0) + return _oid2str[i].printable; + i++; + } + while (_oid2str[i].oid != NULL); + + return 0; +} + +/** + * gnutls_x509_dn_oid_known - This function will return true if the given OID is known + * @oid: holds an Object Identifier in a null terminated string + * + * This function will inform about known DN OIDs. This is useful since functions + * like gnutls_x509_crt_set_dn_by_oid() use the information on known + * OIDs to properly encode their input. Object Identifiers that are not + * known are not encoded by these functions, and their input is stored directly + * into the ASN.1 structure. In that case of unknown OIDs, you have + * the responsibility of DER encoding your data. + * + * Returns 1 on known OIDs and 0 otherwise. + * + **/ +int +gnutls_x509_dn_oid_known (const char *oid) +{ + int i = 0; + + do + { + if (strcmp (_oid2str[i].oid, oid) == 0) + return 1; + i++; + } + while (_oid2str[i].oid != NULL); + + return 0; +} + +/* Returns 1 if the data defined by the OID are of a choice + * type. + */ +int +_gnutls_x509_oid_data_choice (const char *oid) +{ + int i = 0; + + do + { + if (strcmp (_oid2str[i].oid, oid) == 0) + return _oid2str[i].choice; + i++; + } + while (_oid2str[i].oid != NULL); + + return 0; +} + +const char * +_gnutls_x509_oid2ldap_string (const char *oid) +{ + int i = 0; + + do + { + if (strcmp (_oid2str[i].oid, oid) == 0) + return _oid2str[i].ldap_desc; + i++; + } + while (_oid2str[i].oid != NULL); + + return NULL; +} + +/* This function will convert an attribute value, specified by the OID, + * to a string. The result will be a null terminated string. + * + * res may be null. This will just return the res_size, needed to + * hold the string. + */ +int +_gnutls_x509_oid_data2string (const char *oid, + void *value, + int value_size, char *res, size_t * res_size) +{ + char str[MAX_STRING_LEN], tmpname[128]; + const char *ANAME = NULL; + int CHOICE = -1, len = -1, result; + ASN1_TYPE tmpasn = ASN1_TYPE_EMPTY; + char asn1_err[MAX_ERROR_DESCRIPTION_SIZE] = ""; + + if (value == NULL || value_size <= 0 || res_size == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + if (_gnutls_x509_oid_data_printable (oid) == 0) + { + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + ANAME = asn1_find_structure_from_oid (_gnutls_get_pkix (), oid); + CHOICE = _gnutls_x509_oid_data_choice (oid); + + if (ANAME == NULL) + { + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + _gnutls_str_cpy (str, sizeof (str), "PKIX1."); + _gnutls_str_cat (str, sizeof (str), ANAME); + + if ((result = asn1_create_element (_gnutls_get_pkix (), str, + &tmpasn)) != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + if ((result = asn1_der_decoding (&tmpasn, value, value_size, asn1_err)) + != ASN1_SUCCESS) + { + gnutls_assert (); + _gnutls_x509_log ("asn1_der_decoding: %s:%s\n", str, asn1_err); + asn1_delete_structure (&tmpasn); + return _gnutls_asn2err (result); + } + + /* If this is a choice then we read the choice. Otherwise it + * is the value; + */ + len = sizeof (str) - 1; + if ((result = asn1_read_value (tmpasn, "", str, &len)) != ASN1_SUCCESS) + { /* CHOICE */ + gnutls_assert (); + asn1_delete_structure (&tmpasn); + return _gnutls_asn2err (result); + } + + if (CHOICE == 0) + { + str[len] = 0; + + if (res) + _gnutls_str_cpy (res, *res_size, str); + *res_size = len; + + asn1_delete_structure (&tmpasn); + } + else + { /* CHOICE */ + int non_printable = 0, teletex = 0; + str[len] = 0; + + /* Note that we do not support strings other than + * UTF-8 (thus ASCII as well). + */ + if (strcmp (str, "printableString") != 0 + && strcmp (str, "ia5String") != 0 + && strcmp (str, "utf8String") != 0) + { + non_printable = 1; + } + if (strcmp (str, "teletexString") == 0) + teletex = 1; + + _gnutls_str_cpy (tmpname, sizeof (tmpname), str); + + len = sizeof (str) - 1; + if ((result = asn1_read_value (tmpasn, tmpname, str, &len)) + != ASN1_SUCCESS) + { + asn1_delete_structure (&tmpasn); + return _gnutls_asn2err (result); + } + + asn1_delete_structure (&tmpasn); + + if (teletex != 0) + { + int ascii = 0, i; + /* HACK: if the teletex string contains only ascii + * characters then treat it as printable. + */ + for (i = 0; i < len; i++) + if (!isascii (str[i])) + ascii = 1; + + if (ascii == 0) + non_printable = 0; + } + + if (res) + { + if (non_printable == 0) + { + str[len] = 0; + _gnutls_str_cpy (res, *res_size, str); + *res_size = len; + } + else + { + result = _gnutls_x509_data2hex (str, len, res, res_size); + if (result < 0) + { + gnutls_assert (); + return result; + } + } + } + + } + + return 0; +} + +/* Converts a data string to an LDAP rfc2253 hex string + * something like '#01020304' + */ +int +_gnutls_x509_data2hex (const opaque * data, + size_t data_size, opaque * out, size_t * sizeof_out) +{ + char *res; + char escaped[MAX_STRING_LEN]; + + if (2 * data_size + 1 > MAX_STRING_LEN) + { + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + res = _gnutls_bin2hex (data, data_size, escaped, sizeof (escaped)); + + if (res) + { + unsigned int size = strlen (res) + 1; + if (size + 1 > *sizeof_out) + { + *sizeof_out = size; + return GNUTLS_E_SHORT_MEMORY_BUFFER; + } + *sizeof_out = size; /* -1 for the null +1 for the '#' */ + + if (out) + { + strcpy (out, "#"); + strcat (out, res); + } + + return 0; + } + else + { + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + return 0; +} + +/* TIME functions + * Convertions between generalized or UTC time to time_t + * + */ + +/* This is an emulations of the struct tm. + * Since we do not use libc's functions, we don't need to + * depend on the libc structure. + */ +typedef struct fake_tm +{ + int tm_mon; + int tm_year; /* FULL year - ie 1971 */ + int tm_mday; + int tm_hour; + int tm_min; + int tm_sec; +} fake_tm; + +/* The mktime_utc function is due to Russ Allbery (rra@stanford.edu), + * who placed it under public domain: + */ + +/* The number of days in each month. + */ +static const int MONTHDAYS[] = { 31, + 28, + 31, + 30, + 31, + 30, + 31, + 31, + 30, + 31, + 30, + 31 +}; + +/* Whether a given year is a leap year. */ +#define ISLEAP(year) \ + (((year) % 4) == 0 && (((year) % 100) != 0 || ((year) % 400) == 0)) + +/* + ** Given a struct tm representing a calendar time in UTC, convert it to + ** seconds since epoch. Returns (time_t) -1 if the time is not + ** convertable. Note that this function does not canonicalize the provided + ** struct tm, nor does it allow out of range values or years before 1970. + */ +static time_t +mktime_utc (const struct fake_tm *tm) +{ + time_t result = 0; + int i; + + /* We do allow some ill-formed dates, but we don't do anything special + * with them and our callers really shouldn't pass them to us. Do + * explicitly disallow the ones that would cause invalid array accesses + * or other algorithm problems. + */ + if (tm->tm_mon < 0 || tm->tm_mon > 11 || tm->tm_year < 1970) + return (time_t) - 1; + + /* Convert to a time_t. + */ + for (i = 1970; i < tm->tm_year; i++) + result += 365 + ISLEAP (i); + for (i = 0; i < tm->tm_mon; i++) + result += MONTHDAYS[i]; + if (tm->tm_mon > 1 && ISLEAP (tm->tm_year)) + result++; + result = 24 * (result + tm->tm_mday - 1) + tm->tm_hour; + result = 60 * result + tm->tm_min; + result = 60 * result + tm->tm_sec; + return result; +} + +/* this one will parse dates of the form: + * month|day|hour|minute|sec* (2 chars each) + * and year is given. Returns a time_t date. + */ +time_t +_gnutls_x509_time2gtime (const char *ttime, int year) +{ + char xx[3]; + struct fake_tm etime; + time_t ret; + + if (strlen (ttime) < 8) + { + gnutls_assert (); + return (time_t) - 1; + } + + etime.tm_year = year; + + /* In order to work with 32 bit + * time_t. + */ + if (sizeof (time_t) <= 4 && etime.tm_year >= 2038) + return (time_t) 2145914603; /* 2037-12-31 23:23:23 */ + + xx[2] = 0; + + /* get the month + */ + memcpy (xx, ttime, 2); /* month */ + etime.tm_mon = atoi (xx) - 1; + ttime += 2; + + /* get the day + */ + memcpy (xx, ttime, 2); /* day */ + etime.tm_mday = atoi (xx); + ttime += 2; + + /* get the hour + */ + memcpy (xx, ttime, 2); /* hour */ + etime.tm_hour = atoi (xx); + ttime += 2; + + /* get the minutes + */ + memcpy (xx, ttime, 2); /* minutes */ + etime.tm_min = atoi (xx); + ttime += 2; + + if (strlen (ttime) >= 2) + { + memcpy (xx, ttime, 2); + etime.tm_sec = atoi (xx); + ttime += 2; + } + else + etime.tm_sec = 0; + + ret = mktime_utc (&etime); + + return ret; +} + +/* returns a time_t value that contains the given time. + * The given time is expressed as: + * YEAR(2)|MONTH(2)|DAY(2)|HOUR(2)|MIN(2)|SEC(2)* + * + * (seconds are optional) + */ +time_t +_gnutls_x509_utcTime2gtime (const char *ttime) +{ + char xx[3]; + int year; + + if (strlen (ttime) < 10) + { + gnutls_assert (); + return (time_t) - 1; + } + xx[2] = 0; + /* get the year + */ + memcpy (xx, ttime, 2); /* year */ + year = atoi (xx); + ttime += 2; + + if (year > 49) + year += 1900; + else + year += 2000; + + return _gnutls_x509_time2gtime (ttime, year); +} + +/* returns a time value that contains the given time. + * The given time is expressed as: + * YEAR(2)|MONTH(2)|DAY(2)|HOUR(2)|MIN(2)|SEC(2) + */ +int +_gnutls_x509_gtime2utcTime (time_t gtime, char *str_time, int str_time_size) +{ + size_t ret; + +#ifdef HAVE_GMTIME_R + struct tm _tm; + + gmtime_r (>ime, &_tm); + + ret = strftime (str_time, str_time_size, "%y%m%d%H%M%SZ", &_tm); +#else + struct tm *_tm; + + _tm = gmtime (>ime); + + ret = strftime (str_time, str_time_size, "%y%m%d%H%M%SZ", _tm); +#endif + + if (!ret) + { + gnutls_assert (); + return GNUTLS_E_SHORT_MEMORY_BUFFER; + } + + return 0; + +} + +/* returns a time_t value that contains the given time. + * The given time is expressed as: + * YEAR(4)|MONTH(2)|DAY(2)|HOUR(2)|MIN(2)|SEC(2)* + */ +time_t +_gnutls_x509_generalTime2gtime (const char *ttime) +{ + char xx[5]; + int year; + + if (strlen (ttime) < 12) + { + gnutls_assert (); + return (time_t) - 1; + } + + if (strchr (ttime, 'Z') == 0) + { + gnutls_assert (); + /* sorry we don't support it yet + */ + return (time_t) - 1; + } + xx[4] = 0; + + /* get the year + */ + memcpy (xx, ttime, 4); /* year */ + year = atoi (xx); + ttime += 4; + + return _gnutls_x509_time2gtime (ttime, year); + +} + +/* Extracts the time in time_t from the ASN1_TYPE given. When should + * be something like "tbsCertList.thisUpdate". + */ +#define MAX_TIME 64 +time_t +_gnutls_x509_get_time (ASN1_TYPE c2, const char *when) +{ + char ttime[MAX_TIME]; + char name[128]; + time_t c_time = (time_t) - 1; + int len, result; + + _gnutls_str_cpy (name, sizeof (name), when); + + len = sizeof (ttime) - 1; + if ((result = asn1_read_value (c2, name, ttime, &len)) < 0) + { + gnutls_assert (); + return (time_t) (-1); + } + + /* CHOICE */ + if (strcmp (ttime, "generalTime") == 0) + { + + _gnutls_str_cat (name, sizeof (name), ".generalTime"); + len = sizeof (ttime) - 1; + result = asn1_read_value (c2, name, ttime, &len); + if (result == ASN1_SUCCESS) + c_time = _gnutls_x509_generalTime2gtime (ttime); + } + else + { /* UTCTIME */ + + _gnutls_str_cat (name, sizeof (name), ".utcTime"); + len = sizeof (ttime) - 1; + result = asn1_read_value (c2, name, ttime, &len); + if (result == ASN1_SUCCESS) + c_time = _gnutls_x509_utcTime2gtime (ttime); + } + + /* We cannot handle dates after 2031 in 32 bit machines. + * a time_t of 64bits has to be used. + */ + + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return (time_t) (-1); + } + return c_time; +} + +/* Sets the time in time_t in the ASN1_TYPE given. Where should + * be something like "tbsCertList.thisUpdate". + */ +int +_gnutls_x509_set_time (ASN1_TYPE c2, const char *where, time_t tim) +{ + char str_time[MAX_TIME]; + char name[128]; + int result, len; + + _gnutls_str_cpy (name, sizeof (name), where); + + if ((result = asn1_write_value (c2, name, "utcTime", 1)) < 0) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + result = _gnutls_x509_gtime2utcTime (tim, str_time, sizeof (str_time)); + if (result < 0) + { + gnutls_assert (); + return result; + } + + _gnutls_str_cat (name, sizeof (name), ".utcTime"); + + len = strlen (str_time); + result = asn1_write_value (c2, name, str_time, len); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + return 0; +} + +gnutls_x509_subject_alt_name_t +_gnutls_x509_san_find_type (char *str_type) +{ + if (strcmp (str_type, "dNSName") == 0) + return GNUTLS_SAN_DNSNAME; + if (strcmp (str_type, "rfc822Name") == 0) + return GNUTLS_SAN_RFC822NAME; + if (strcmp (str_type, "uniformResourceIdentifier") == 0) + return GNUTLS_SAN_URI; + if (strcmp (str_type, "iPAddress") == 0) + return GNUTLS_SAN_IPADDRESS; + if (strcmp (str_type, "otherName") == 0) + return GNUTLS_SAN_OTHERNAME; + if (strcmp (str_type, "directoryName") == 0) + return GNUTLS_SAN_DN; + return (gnutls_x509_subject_alt_name_t) - 1; +} + +/* A generic export function. Will export the given ASN.1 encoded data + * to PEM or DER raw data. + */ +int +_gnutls_x509_export_int (ASN1_TYPE asn1_data, + gnutls_x509_crt_fmt_t format, + char *pem_header, + unsigned char *output_data, + size_t * output_data_size) +{ + int result, len; + + if (format == GNUTLS_X509_FMT_DER) + { + + if (output_data == NULL) + *output_data_size = 0; + + len = *output_data_size; + + if ((result = asn1_der_coding (asn1_data, "", output_data, &len, + NULL)) != ASN1_SUCCESS) + { + *output_data_size = len; + if (result == ASN1_MEM_ERROR) + { + return GNUTLS_E_SHORT_MEMORY_BUFFER; + } + gnutls_assert (); + return _gnutls_asn2err (result); + } + + *output_data_size = len; + + } + else + { /* PEM */ + opaque *out; + gnutls_datum_t tmp; + + result = _gnutls_x509_der_encode (asn1_data, "", &tmp, 0); + if (result < 0) + { + gnutls_assert (); + return result; + } + + result = _gnutls_fbase64_encode (pem_header, tmp.data, tmp.size, &out); + + _gnutls_free_datum (&tmp); + + if (result < 0) + { + gnutls_assert (); + return result; + } + + if (result == 0) + { /* oooops */ + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + if ((unsigned) result > *output_data_size) + { + gnutls_assert (); + gnutls_free (out); + *output_data_size = result; + return GNUTLS_E_SHORT_MEMORY_BUFFER; + } + + *output_data_size = result; + + if (output_data) + { + memcpy (output_data, out, result); + + /* do not include the null character into output size. + */ + *output_data_size = result - 1; + } + gnutls_free (out); + + } + + return 0; +} + +/* Decodes an octet string. Leave string_type null for a normal + * octet string. Otherwise put something like BMPString, PrintableString + * etc. + */ +int +_gnutls_x509_decode_octet_string (const char *string_type, + const opaque * der, + size_t der_size, + opaque * output, size_t * output_size) +{ + ASN1_TYPE c2 = ASN1_TYPE_EMPTY; + int result, tmp_output_size; + char strname[64]; + + if (string_type == NULL) + _gnutls_str_cpy (strname, sizeof (strname), "PKIX1.pkcs-7-Data"); + else + { + _gnutls_str_cpy (strname, sizeof (strname), "PKIX1."); + _gnutls_str_cat (strname, sizeof (strname), string_type); + } + + if ((result = + asn1_create_element (_gnutls_get_pkix (), strname, + &c2)) != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + result = asn1_der_decoding (&c2, der, der_size, NULL); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + tmp_output_size = *output_size; + result = asn1_read_value (c2, "", output, &tmp_output_size); + *output_size = tmp_output_size; + + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + return 0; + +cleanup:if (c2) + asn1_delete_structure (&c2); + + return result; +} + +/* Reads a value from an ASN1 tree, and puts the output + * in an allocated variable in the given datum. + * flags == 0 do nothing with the DER output + * flags == 1 parse the DER output as OCTET STRING + * flags == 2 the value is a BIT STRING + */ +int +_gnutls_x509_read_value (ASN1_TYPE c, + const char *root, gnutls_datum_t * ret, int flags) +{ + int len = 0, result; + size_t slen; + opaque *tmp = NULL; + + result = asn1_read_value (c, root, NULL, &len); + if (result != ASN1_MEM_ERROR) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + return result; + } + + if (flags == 2) + len /= 8; + + tmp = gnutls_malloc (len); + if (tmp == NULL) + { + gnutls_assert (); + result = GNUTLS_E_MEMORY_ERROR; + goto cleanup; + } + + result = asn1_read_value (c, root, tmp, &len); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + if (flags == 2) + len /= 8; + + /* Extract the OCTET STRING. + */ + + if (flags == 1) + { + slen = len; + result = _gnutls_x509_decode_octet_string (NULL, tmp, slen, tmp, &slen); + if (result < 0) + { + gnutls_assert (); + goto cleanup; + } + len = slen; + } + + ret->data = tmp; + ret->size = len; + + return 0; + +cleanup:gnutls_free (tmp); + return result; + +} + +/* DER Encodes the src ASN1_TYPE and stores it to + * the given datum. If str is non null then the data are encoded as + * an OCTET STRING. + */ +int +_gnutls_x509_der_encode (ASN1_TYPE src, + const char *src_name, gnutls_datum_t * res, int str) +{ + int size, result; + int asize; + opaque *data = NULL; + ASN1_TYPE c2 = ASN1_TYPE_EMPTY; + + size = 0; + result = asn1_der_coding (src, src_name, NULL, &size, NULL); + if (result != ASN1_MEM_ERROR) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + /* allocate data for the der + */ + + if (str) + size += 16; /* for later to include the octet tags */ + asize = size; + + data = gnutls_malloc (size); + if (data == NULL) + { + gnutls_assert (); + result = GNUTLS_E_MEMORY_ERROR; + goto cleanup; + } + + result = asn1_der_coding (src, src_name, data, &size, NULL); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + if (str) + { + if ((result = + asn1_create_element (_gnutls_get_pkix (), "PKIX1.pkcs-7-Data", + &c2)) != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + result = asn1_write_value (c2, "", data, size); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + result = asn1_der_coding (c2, "", data, &asize, NULL); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + size = asize; + + asn1_delete_structure (&c2); + } + + res->data = data; + res->size = size; + return 0; + +cleanup:gnutls_free (data); + asn1_delete_structure (&c2); + return result; + +} + +/* DER Encodes the src ASN1_TYPE and stores it to + * dest in dest_name. Useful to encode something and store it + * as OCTET. If str is non null then the data are encoded as + * an OCTET STRING. + */ +int +_gnutls_x509_der_encode_and_copy (ASN1_TYPE src, + const char *src_name, + ASN1_TYPE dest, + const char *dest_name, int str) +{ + int result; + gnutls_datum_t encoded; + + result = _gnutls_x509_der_encode (src, src_name, &encoded, str); + + if (result < 0) + { + gnutls_assert (); + return result; + } + + /* Write the data. + */ + result = asn1_write_value (dest, dest_name, encoded.data, encoded.size); + + _gnutls_free_datum (&encoded); + + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + return 0; +} + +/* Writes the value of the datum in the given ASN1_TYPE. If str is non + * zero it encodes it as OCTET STRING. + */ +int +_gnutls_x509_write_value (ASN1_TYPE c, + const char *root, + const gnutls_datum_t * data, int str) +{ + int result; + int asize; + ASN1_TYPE c2 = ASN1_TYPE_EMPTY; + gnutls_datum_t val; + + asize = data->size + 16; + + val.data = gnutls_malloc (asize); + if (val.data == NULL) + { + gnutls_assert (); + result = GNUTLS_E_MEMORY_ERROR; + goto cleanup; + } + + if (str) + { + /* Convert it to OCTET STRING + */ + if ((result = + asn1_create_element (_gnutls_get_pkix (), "PKIX1.pkcs-7-Data", + &c2)) != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + result = asn1_write_value (c2, "", data->data, data->size); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + result = _gnutls_x509_der_encode (c2, "", &val, 0); + if (result < 0) + { + gnutls_assert (); + goto cleanup; + } + + } + else + { + val.data = data->data; + val.size = data->size; + } + + /* Write the data. + */ + result = asn1_write_value (c, root, val.data, val.size); + + if (val.data != data->data) + _gnutls_free_datum (&val); + + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + return 0; + +cleanup:if (val.data != data->data) + _gnutls_free_datum (&val); + return result; +} + +/* Encodes and copies the private key parameters into a + * subjectPublicKeyInfo structure. + * + */ +int +_gnutls_x509_encode_and_copy_PKI_params (ASN1_TYPE dst, + const char *dst_name, + gnutls_pk_algorithm_t + pk_algorithm, + mpi_t * params, int params_size) +{ + const char *pk; + gnutls_datum_t der = { NULL, + 0 + }; + int result; + char name[128]; + + pk = _gnutls_x509_pk_to_oid (pk_algorithm); + if (pk == NULL) + { + gnutls_assert (); + return GNUTLS_E_UNKNOWN_PK_ALGORITHM; + } + + /* write the OID + */ + _gnutls_str_cpy (name, sizeof (name), dst_name); + _gnutls_str_cat (name, sizeof (name), ".algorithm.algorithm"); + result = asn1_write_value (dst, name, pk, 1); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + if (pk_algorithm == GNUTLS_PK_RSA) + { + /* disable parameters, which are not used in RSA. + */ + _gnutls_str_cpy (name, sizeof (name), dst_name); + _gnutls_str_cat (name, sizeof (name), ".algorithm.parameters"); + result = asn1_write_value (dst, name, NULL, 0); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + result = _gnutls_x509_write_rsa_params (params, params_size, &der); + if (result < 0) + { + gnutls_assert (); + return result; + } + + /* Write the DER parameters. (in bits) + */ + _gnutls_str_cpy (name, sizeof (name), dst_name); + _gnutls_str_cat (name, sizeof (name), ".subjectPublicKey"); + result = asn1_write_value (dst, name, der.data, der.size * 8); + + _gnutls_free_datum (&der); + + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + } + else + return GNUTLS_E_UNIMPLEMENTED_FEATURE; + + return 0; +} + +/* Reads and returns the PK algorithm of the given certificate-like + * ASN.1 structure. src_name should be something like "tbsCertificate.subjectPublicKeyInfo". + */ +int +_gnutls_x509_get_pk_algorithm (ASN1_TYPE src, + const char *src_name, unsigned int *bits) +{ + int result; + opaque *str = NULL; + int algo; + char oid[64]; + int len; + mpi_t params[MAX_PUBLIC_PARAMS_SIZE]; + char name[128]; + + _gnutls_str_cpy (name, sizeof (name), src_name); + _gnutls_str_cat (name, sizeof (name), ".algorithm.algorithm"); + + len = sizeof (oid); + result = asn1_read_value (src, name, oid, &len); + + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + algo = _gnutls_x509_oid2pk_algorithm (oid); + + if (bits == NULL) + { + gnutls_free (str); + return algo; + } + + /* Now read the parameters' bits + */ + _gnutls_str_cpy (name, sizeof (name), src_name); + _gnutls_str_cat (name, sizeof (name), ".subjectPublicKey"); + + len = 0; + result = asn1_read_value (src, name, NULL, &len); + if (result != ASN1_MEM_ERROR) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + if (len % 8 != 0) + { + gnutls_assert (); + return GNUTLS_E_CERTIFICATE_ERROR; + } + + len /= 8; + + str = gnutls_malloc (len); + if (str == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + _gnutls_str_cpy (name, sizeof (name), src_name); + _gnutls_str_cat (name, sizeof (name), ".subjectPublicKey"); + + result = asn1_read_value (src, name, str, &len); + + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + gnutls_free (str); + return _gnutls_asn2err (result); + } + + len /= 8; + + switch (algo) + { + case GNUTLS_PK_RSA: + { + if ((result = _gnutls_x509_read_rsa_params (str, len, params)) < 0) + { + gnutls_assert (); + return result; + } + + bits[0] = _gnutls_mpi_get_nbits (params[0]); + + _gnutls_mpi_release (¶ms[0]); + _gnutls_mpi_release (¶ms[1]); + } + break; + default: + _gnutls_x509_log + ("_gnutls_x509_get_pk_algorithm: unhandled algorithm %d\n", algo); + } + + gnutls_free (str); + return algo; +} + +/* Reads the DER signed data from the certificate and allocates space and + * returns them into signed_data. + */ +int +_gnutls_x509_get_signed_data (ASN1_TYPE src, + const char *src_name, + gnutls_datum_t * signed_data) +{ + gnutls_datum_t der; + int start, end, result; + + result = _gnutls_x509_der_encode (src, "", &der, 0); + if (result < 0) + { + gnutls_assert (); + return result; + } + + /* Get the signed data + */ + result = asn1_der_decoding_startEnd (src, der.data, der.size, src_name, + &start, &end); + if (result != ASN1_SUCCESS) + { + result = _gnutls_asn2err (result); + gnutls_assert (); + goto cleanup; + } + + result = _gnutls_set_datum (signed_data, &der.data[start], end - start + 1); + + if (result < 0) + { + gnutls_assert (); + goto cleanup; + } + + result = 0; + +cleanup:_gnutls_free_datum (&der); + + return result; +} + +/* Reads the DER signature from the certificate and allocates space and + * returns them into signed_data. + */ +int +_gnutls_x509_get_signature (ASN1_TYPE src, + const char *src_name, gnutls_datum_t * signature) +{ + int bits, result, len; + + signature->data = NULL; + signature->size = 0; + + /* Read the signature + */ + bits = 0; + result = asn1_read_value (src, src_name, NULL, &bits); + + if (result != ASN1_MEM_ERROR) + { + result = _gnutls_asn2err (result); + gnutls_assert (); + goto cleanup; + } + + if (bits % 8 != 0) + { + gnutls_assert (); + result = GNUTLS_E_CERTIFICATE_ERROR; + goto cleanup; + } + + len = bits / 8; + + signature->data = gnutls_malloc (len); + if (signature->data == NULL) + { + gnutls_assert (); + result = GNUTLS_E_MEMORY_ERROR; + return result; + } + + /* read the bit string of the signature + */ + bits = len; + result = asn1_read_value (src, src_name, signature->data, &bits); + + if (result != ASN1_SUCCESS) + { + result = _gnutls_asn2err (result); + gnutls_assert (); + goto cleanup; + } + + signature->size = len; + + return 0; + +cleanup:return result; +} diff --git a/src/daemon/https/x509/common.h b/src/daemon/https/x509/common.h new file mode 100644 index 00000000..0b696810 --- /dev/null +++ b/src/daemon/https/x509/common.h @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2007 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +#ifndef COMMON_H +# define COMMON_H + +#include +#include + +#define MAX_STRING_LEN 512 + +#define GNUTLS_XML_SHOW_ALL 1 + +#define PEM_CRL "X509 CRL" +#define PEM_X509_CERT "X509 CERTIFICATE" +#define PEM_X509_CERT2 "CERTIFICATE" +#define PEM_PKCS7 "PKCS7" +#define PEM_PKCS12 "PKCS12" + +/* public key algorithm's OIDs + */ +#define PK_PKIX1_RSA_OID "1.2.840.113549.1.1.1" +#define PK_DSA_OID "1.2.840.10040.4.1" +#define PK_GOST_R3410_94_OID "1.2.643.2.2.20" +#define PK_GOST_R3410_2001_OID "1.2.643.2.2.19" + +/* signature OIDs + */ +#define SIG_DSA_SHA1_OID "1.2.840.10040.4.3" +#define SIG_RSA_MD5_OID "1.2.840.113549.1.1.4" +#define SIG_RSA_MD2_OID "1.2.840.113549.1.1.2" +#define SIG_RSA_SHA1_OID "1.2.840.113549.1.1.5" +#define SIG_RSA_SHA256_OID "1.2.840.113549.1.1.11" +#define SIG_RSA_SHA384_OID "1.2.840.113549.1.1.12" +#define SIG_RSA_SHA512_OID "1.2.840.113549.1.1.13" +#define SIG_RSA_RMD160_OID "1.3.36.3.3.1.2" +#define SIG_GOST_R3410_94_OID "1.2.643.2.2.4" +#define SIG_GOST_R3410_2001_OID "1.2.643.2.2.3" + +time_t _gnutls_x509_utcTime2gtime (const char *ttime); +time_t _gnutls_x509_generalTime2gtime (const char *ttime); +int _gnutls_x509_set_time (ASN1_TYPE c2, const char *where, time_t tim); + +int _gnutls_x509_decode_octet_string (const char *string_type, + const opaque * der, size_t der_size, + opaque * output, size_t * output_size); +int _gnutls_x509_oid_data2string (const char *OID, void *value, + int value_size, char *res, + size_t * res_size); +int _gnutls_x509_data2hex (const opaque * data, size_t data_size, + opaque * out, size_t * sizeof_out); + +const char *_gnutls_x509_oid2ldap_string (const char *OID); + +int _gnutls_x509_oid_data_choice (const char *OID); +int _gnutls_x509_oid_data_printable (const char *OID); + +time_t _gnutls_x509_get_time (ASN1_TYPE c2, const char *when); + +gnutls_x509_subject_alt_name_t _gnutls_x509_san_find_type (char *str_type); + +int _gnutls_x509_der_encode_and_copy (ASN1_TYPE src, const char *src_name, + ASN1_TYPE dest, const char *dest_name, + int str); +int _gnutls_x509_der_encode (ASN1_TYPE src, const char *src_name, + gnutls_datum_t * res, int str); + +int _gnutls_x509_export_int (ASN1_TYPE asn1_data, + gnutls_x509_crt_fmt_t format, char *pem_header, + unsigned char *output_data, + size_t * output_data_size); + +int _gnutls_x509_read_value (ASN1_TYPE c, const char *root, + gnutls_datum_t * ret, int str); +int _gnutls_x509_write_value (ASN1_TYPE c, const char *root, + const gnutls_datum_t * data, int str); + +int _gnutls_x509_encode_and_write_attribute (const char *given_oid, + ASN1_TYPE asn1_struct, + const char *where, + const void *data, + int sizeof_data, int multi); +int _gnutls_x509_decode_and_read_attribute (ASN1_TYPE asn1_struct, + const char *where, char *oid, + int oid_size, + gnutls_datum_t * value, int multi, + int octet); + +int _gnutls_x509_get_pk_algorithm (ASN1_TYPE src, const char *src_name, + unsigned int *bits); + +int _gnutls_x509_encode_and_copy_PKI_params (ASN1_TYPE dst, + const char *dst_name, + gnutls_pk_algorithm_t + pk_algorithm, mpi_t * params, + int params_size); +int _gnutls_asn1_copy_node (ASN1_TYPE * dst, const char *dst_name, + ASN1_TYPE src, const char *src_name); + +int _gnutls_x509_get_signed_data (ASN1_TYPE src, const char *src_name, + gnutls_datum_t * signed_data); +int _gnutls_x509_get_signature (ASN1_TYPE src, const char *src_name, + gnutls_datum_t * signature); + +#endif diff --git a/src/daemon/https/x509/crl.c b/src/daemon/https/x509/crl.c new file mode 100644 index 00000000..d3b5c954 --- /dev/null +++ b/src/daemon/https/x509/crl.c @@ -0,0 +1,705 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2006, 2007 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +#include +#include + +#ifdef ENABLE_PKI + +#include +#include +#include +#include +#include +#include +#include + +/** + * gnutls_x509_crl_init - This function initializes a gnutls_x509_crl_t structure + * @crl: The structure to be initialized + * + * This function will initialize a CRL structure. CRL stands for + * Certificate Revocation List. A revocation list usually contains + * lists of certificate serial numbers that have been revoked + * by an Authority. The revocation lists are always signed with + * the authority's private key. + * + * Returns 0 on success. + * + **/ +int +gnutls_x509_crl_init (gnutls_x509_crl_t * crl) +{ + *crl = gnutls_calloc (1, sizeof (gnutls_x509_crl_int)); + + if (*crl) + { + int result = asn1_create_element (_gnutls_get_pkix (), + "PKIX1.CertificateList", + &(*crl)->crl); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + gnutls_free (*crl); + return _gnutls_asn2err (result); + } + return 0; /* success */ + } + return GNUTLS_E_MEMORY_ERROR; +} + +/** + * gnutls_x509_crl_deinit - This function deinitializes memory used by a gnutls_x509_crl_t structure + * @crl: The structure to be initialized + * + * This function will deinitialize a CRL structure. + * + **/ +void +gnutls_x509_crl_deinit (gnutls_x509_crl_t crl) +{ + if (!crl) + return; + + if (crl->crl) + asn1_delete_structure (&crl->crl); + + gnutls_free (crl); +} + +/** + * gnutls_x509_crl_import - This function will import a DER or PEM encoded CRL + * @crl: The structure to store the parsed CRL. + * @data: The DER or PEM encoded CRL. + * @format: One of DER or PEM + * + * This function will convert the given DER or PEM encoded CRL + * to the native gnutls_x509_crl_t format. The output will be stored in 'crl'. + * + * If the CRL is PEM encoded it should have a header of "X509 CRL". + * + * Returns 0 on success. + * + **/ +int +gnutls_x509_crl_import (gnutls_x509_crl_t crl, + const gnutls_datum_t * data, + gnutls_x509_crt_fmt_t format) +{ + int result = 0, need_free = 0; + gnutls_datum_t _data; + + _data.data = data->data; + _data.size = data->size; + + if (crl == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + /* If the CRL is in PEM format then decode it + */ + if (format == GNUTLS_X509_FMT_PEM) + { + opaque *out; + + result = _gnutls_fbase64_decode (PEM_CRL, data->data, data->size, &out); + + if (result <= 0) + { + if (result == 0) + result = GNUTLS_E_INTERNAL_ERROR; + gnutls_assert (); + return result; + } + + _data.data = out; + _data.size = result; + + need_free = 1; + } + + + result = asn1_der_decoding (&crl->crl, _data.data, _data.size, NULL); + if (result != ASN1_SUCCESS) + { + result = _gnutls_asn2err (result); + gnutls_assert (); + goto cleanup; + } + + if (need_free) + _gnutls_free_datum (&_data); + + return 0; + +cleanup: + if (need_free) + _gnutls_free_datum (&_data); + return result; +} + + +/** + * gnutls_x509_crl_get_issuer_dn - This function returns the CRL's issuer distinguished name + * @crl: should contain a gnutls_x509_crl_t structure + * @buf: a pointer to a structure to hold the peer's name (may be null) + * @sizeof_buf: initially holds the size of @buf + * + * This function will copy the name of the CRL issuer in the provided buffer. The name + * will be in the form "C=xxxx,O=yyyy,CN=zzzz" as described in RFC2253. The output + * string will be ASCII or UTF-8 encoded, depending on the certificate data. + * + * If buf is null then only the size will be filled. + * + * Returns GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is not long enough, and + * in that case the sizeof_buf will be updated with the required size, and + * 0 on success. + * + **/ +int +gnutls_x509_crl_get_issuer_dn (const gnutls_x509_crl_t crl, char *buf, + size_t * sizeof_buf) +{ + if (crl == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_parse_dn (crl->crl, + "tbsCertList.issuer.rdnSequence", + buf, sizeof_buf); +} + +/** + * gnutls_x509_crl_get_issuer_dn_by_oid - This function returns the CRL's issuer distinguished name + * @crl: should contain a gnutls_x509_crl_t structure + * @oid: holds an Object Identified in null terminated string + * @indx: In case multiple same OIDs exist in the RDN, this specifies which to send. Use zero to get the first one. + * @raw_flag: If non zero returns the raw DER data of the DN part. + * @buf: a pointer to a structure to hold the peer's name (may be null) + * @sizeof_buf: initially holds the size of @buf + * + * This function will extract the part of the name of the CRL issuer specified + * by the given OID. The output will be encoded as described in RFC2253. The output + * string will be ASCII or UTF-8 encoded, depending on the certificate data. + * + * Some helper macros with popular OIDs can be found in gnutls/x509.h + * If raw flag is zero, this function will only return known OIDs as text. Other OIDs + * will be DER encoded, as described in RFC2253 -- in hex format with a '\#' prefix. + * You can check about known OIDs using gnutls_x509_dn_oid_known(). + * + * If buf is null then only the size will be filled. + * + * Returns GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is not long enough, and + * in that case the sizeof_buf will be updated with the required size, + * and 0 on success. + * + **/ +int +gnutls_x509_crl_get_issuer_dn_by_oid (gnutls_x509_crl_t crl, + const char *oid, int indx, + unsigned int raw_flag, void *buf, + size_t * sizeof_buf) +{ + if (crl == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_parse_dn_oid (crl->crl, + "tbsCertList.issuer.rdnSequence", + oid, indx, raw_flag, buf, sizeof_buf); +} + +/** + * gnutls_x509_crl_get_dn_oid - This function returns the Certificate request issuer's distinguished name OIDs + * @crl: should contain a gnutls_x509_crl_t structure + * @indx: Specifies which DN OID to send. Use zero to get the first one. + * @oid: a pointer to a structure to hold the name (may be null) + * @sizeof_oid: initially holds the size of 'oid' + * + * This function will extract the requested OID of the name of the CRL issuer, specified + * by the given index. + * + * If oid is null then only the size will be filled. + * + * Returns GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is not long enough, and + * in that case the sizeof_oid will be updated with the required size. + * On success 0 is returned. + * + **/ +int +gnutls_x509_crl_get_dn_oid (gnutls_x509_crl_t crl, + int indx, void *oid, size_t * sizeof_oid) +{ + if (crl == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_get_dn_oid (crl->crl, + "tbsCertList.issuer.rdnSequence", indx, + oid, sizeof_oid); +} + + +/** + * gnutls_x509_crl_get_signature_algorithm - This function returns the CRL's signature algorithm + * @crl: should contain a gnutls_x509_crl_t structure + * + * This function will return a value of the gnutls_sign_algorithm_t enumeration that + * is the signature algorithm. + * + * Returns a negative value on error. + * + **/ +int +gnutls_x509_crl_get_signature_algorithm (gnutls_x509_crl_t crl) +{ + int result; + gnutls_datum_t sa; + + if (crl == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + /* Read the signature algorithm. Note that parameters are not + * read. They will be read from the issuer's certificate if needed. + */ + + result = + _gnutls_x509_read_value (crl->crl, "signatureAlgorithm.algorithm", + &sa, 0); + + if (result < 0) + { + gnutls_assert (); + return result; + } + + result = _gnutls_x509_oid2sign_algorithm ((const char *) sa.data); + + _gnutls_free_datum (&sa); + + return result; +} + +/** + * gnutls_x509_crl_get_signature - Returns the CRL's signature + * @crl: should contain a gnutls_x509_crl_t structure + * @sig: a pointer where the signature part will be copied (may be null). + * @sizeof_sig: initially holds the size of @sig + * + * This function will extract the signature field of a CRL. + * + * Returns 0 on success, and a negative value on error. + **/ +int +gnutls_x509_crl_get_signature (gnutls_x509_crl_t crl, + char *sig, size_t * sizeof_sig) +{ + int result; + int bits, len; + + if (crl == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + bits = 0; + result = asn1_read_value (crl->crl, "signature", NULL, &bits); + if (result != ASN1_MEM_ERROR) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + if (bits % 8 != 0) + { + gnutls_assert (); + return GNUTLS_E_CERTIFICATE_ERROR; + } + + len = bits / 8; + + if (*sizeof_sig < len) + { + *sizeof_sig = bits / 8; + return GNUTLS_E_SHORT_MEMORY_BUFFER; + } + + result = asn1_read_value (crl->crl, "signature", sig, &len); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + return 0; +} + +/** + * gnutls_x509_crl_get_version - This function returns the CRL's version number + * @crl: should contain a gnutls_x509_crl_t structure + * + * This function will return the version of the specified CRL. + * + * Returns a negative value on error. + * + **/ +int +gnutls_x509_crl_get_version (gnutls_x509_crl_t crl) +{ + opaque version[5]; + int len, result; + + if (crl == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + len = sizeof (version); + if ((result = + asn1_read_value (crl->crl, "tbsCertList.version", version, + &len)) != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + return (int) version[0] + 1; +} + +/** + * gnutls_x509_crl_get_this_update - This function returns the CRL's thisUpdate time + * @crl: should contain a gnutls_x509_crl_t structure + * + * This function will return the time this CRL was issued. + * + * Returns (time_t)-1 on error. + * + **/ +time_t +gnutls_x509_crl_get_this_update (gnutls_x509_crl_t crl) +{ + if (crl == NULL) + { + gnutls_assert (); + return (time_t) - 1; + } + + return _gnutls_x509_get_time (crl->crl, "tbsCertList.thisUpdate"); +} + +/** + * gnutls_x509_crl_get_next_update - This function returns the CRL's nextUpdate time + * @crl: should contain a gnutls_x509_crl_t structure + * + * This function will return the time the next CRL will be issued. + * This field is optional in a CRL so it might be normal to get + * an error instead. + * + * Returns (time_t)-1 on error. + * + **/ +time_t +gnutls_x509_crl_get_next_update (gnutls_x509_crl_t crl) +{ + if (crl == NULL) + { + gnutls_assert (); + return (time_t) - 1; + } + + return _gnutls_x509_get_time (crl->crl, "tbsCertList.nextUpdate"); +} + +/** + * gnutls_x509_crl_get_crt_count - This function returns the number of revoked certificates in a CRL + * @crl: should contain a gnutls_x509_crl_t structure + * + * This function will return the number of revoked certificates in the + * given CRL. + * + * Returns a negative value on failure. + * + **/ +int +gnutls_x509_crl_get_crt_count (gnutls_x509_crl_t crl) +{ + + int count, result; + + if (crl == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + result = + asn1_number_of_elements (crl->crl, + "tbsCertList.revokedCertificates", &count); + + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return 0; /* no certificates */ + } + + return count; +} + +/** + * gnutls_x509_crl_get_crt_serial - This function returns the serial number of a revoked certificate + * @crl: should contain a gnutls_x509_crl_t structure + * @indx: the index of the certificate to extract (starting from 0) + * @serial: where the serial number will be copied + * @serial_size: initially holds the size of serial + * @t: if non null, will hold the time this certificate was revoked + * + * This function will return the serial number of the specified, by + * the index, revoked certificate. + * + * Returns a negative value on failure. + * + **/ +int +gnutls_x509_crl_get_crt_serial (gnutls_x509_crl_t crl, int indx, + unsigned char *serial, + size_t * serial_size, time_t * t) +{ + + int result, _serial_size; + char serial_name[MAX_NAME_SIZE]; + char date_name[MAX_NAME_SIZE]; + + if (crl == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + snprintf (serial_name, sizeof (serial_name), + "tbsCertList.revokedCertificates.?%u.userCertificate", indx + 1); + snprintf (date_name, sizeof (date_name), + "tbsCertList.revokedCertificates.?%u.revocationDate", indx + 1); + + _serial_size = *serial_size; + result = asn1_read_value (crl->crl, serial_name, serial, &_serial_size); + + *serial_size = _serial_size; + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + if (result == ASN1_ELEMENT_NOT_FOUND) + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + return _gnutls_asn2err (result); + } + + if (t) + { + *t = _gnutls_x509_get_time (crl->crl, date_name); + } + + return 0; +} + +/*- + * _gnutls_x509_crl_get_raw_issuer_dn - This function returns the issuer's DN DER encoded + * @crl: should contain a gnutls_x509_crl_t structure + * @dn: will hold the starting point of the DN + * + * This function will return a pointer to the DER encoded DN structure and + * the length. + * + * Returns a negative value on error, and zero on success. + * + -*/ +int +_gnutls_x509_crl_get_raw_issuer_dn (gnutls_x509_crl_t crl, + gnutls_datum_t * dn) +{ + ASN1_TYPE c2 = ASN1_TYPE_EMPTY; + int result, len1; + int start1, end1; + gnutls_datum_t crl_signed_data; + + if (crl == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + /* get the issuer of 'crl' + */ + if ((result = + asn1_create_element (_gnutls_get_pkix (), "PKIX1.TBSCertList", + &c2)) != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + result = + _gnutls_x509_get_signed_data (crl->crl, "tbsCertList", &crl_signed_data); + if (result < 0) + { + gnutls_assert (); + goto cleanup; + } + + result = + asn1_der_decoding (&c2, crl_signed_data.data, crl_signed_data.size, NULL); + if (result != ASN1_SUCCESS) + { + /* couldn't decode DER */ + gnutls_assert (); + asn1_delete_structure (&c2); + result = _gnutls_asn2err (result); + goto cleanup; + } + + result = + asn1_der_decoding_startEnd (c2, crl_signed_data.data, + crl_signed_data.size, "issuer", + &start1, &end1); + + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + len1 = end1 - start1 + 1; + + _gnutls_set_datum (dn, &crl_signed_data.data[start1], len1); + + result = 0; + +cleanup: + asn1_delete_structure (&c2); + _gnutls_free_datum (&crl_signed_data); + return result; +} + +/** + * gnutls_x509_crl_export - This function will export the CRL + * @crl: Holds the revocation list + * @format: the format of output params. One of PEM or DER. + * @output_data: will contain a private key PEM or DER encoded + * @output_data_size: holds the size of output_data (and will be replaced by the actual size of parameters) + * + * This function will export the revocation list to DER or PEM format. + * + * If the buffer provided is not long enough to hold the output, then + * GNUTLS_E_SHORT_MEMORY_BUFFER will be returned. + * + * If the structure is PEM encoded, it will have a header + * of "BEGIN X509 CRL". + * + * Returns 0 on success, and a negative value on failure. + * + **/ +int +gnutls_x509_crl_export (gnutls_x509_crl_t crl, + gnutls_x509_crt_fmt_t format, void *output_data, + size_t * output_data_size) +{ + if (crl == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_export_int (crl->crl, format, PEM_CRL, + output_data, output_data_size); +} + +/*- + * _gnutls_x509_crl_cpy - This function copies a gnutls_x509_crl_t structure + * @dest: The structure where to copy + * @src: The structure to be copied + * + * This function will copy an X.509 certificate structure. + * + * Returns 0 on success. + * + -*/ +int +_gnutls_x509_crl_cpy (gnutls_x509_crl_t dest, gnutls_x509_crl_t src) +{ + int ret; + size_t der_size; + opaque *der; + gnutls_datum_t tmp; + + ret = gnutls_x509_crl_export (src, GNUTLS_X509_FMT_DER, NULL, &der_size); + if (ret != GNUTLS_E_SHORT_MEMORY_BUFFER) + { + gnutls_assert (); + return ret; + } + + der = gnutls_alloca (der_size); + if (der == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + ret = gnutls_x509_crl_export (src, GNUTLS_X509_FMT_DER, der, &der_size); + if (ret < 0) + { + gnutls_assert (); + gnutls_afree (der); + return ret; + } + + tmp.data = der; + tmp.size = der_size; + ret = gnutls_x509_crl_import (dest, &tmp, GNUTLS_X509_FMT_DER); + + gnutls_afree (der); + + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + return 0; + +} + +#endif diff --git a/src/daemon/https/x509/crl_write.c b/src/daemon/https/x509/crl_write.c new file mode 100644 index 00000000..370a492c --- /dev/null +++ b/src/daemon/https/x509/crl_write.c @@ -0,0 +1,316 @@ +/* + * Copyright (C) 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +/* This file contains functions to handle CRL generation. + */ + +#include + +#ifdef ENABLE_PKI + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void disable_optional_stuff (gnutls_x509_crl_t crl); + +/** + * gnutls_x509_crl_set_version - This function will set the CRL version + * @crl: should contain a gnutls_x509_crl_t structure + * @version: holds the version number. For CRLv1 crls must be 1. + * + * This function will set the version of the CRL. This + * must be one for CRL version 1, and so on. The CRLs generated + * by gnutls should have a version number of 2. + * + * Returns 0 on success. + * + **/ +int +gnutls_x509_crl_set_version (gnutls_x509_crl_t crl, unsigned int version) +{ + int result; + char null = version; + + if (crl == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + null -= 1; + if (null < 0) + null = 0; + + result = asn1_write_value (crl->crl, "tbsCertList.version", &null, 1); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + return 0; +} + +/** + * gnutls_x509_crl_sign2 - This function will sign a CRL with a key + * @crl: should contain a gnutls_x509_crl_t structure + * @issuer: is the certificate of the certificate issuer + * @issuer_key: holds the issuer's private key + * @dig: The message digest to use. GNUTLS_DIG_SHA1 is the safe choice unless you know what you're doing. + * @flags: must be 0 + * + * This function will sign the CRL with the issuer's private key, and + * will copy the issuer's information into the CRL. + * + * This must be the last step in a certificate CRL since all + * the previously set parameters are now signed. + * + * Returns 0 on success. + * + **/ +int +gnutls_x509_crl_sign2 (gnutls_x509_crl_t crl, gnutls_x509_crt_t issuer, + gnutls_x509_privkey_t issuer_key, + gnutls_digest_algorithm_t dig, unsigned int flags) +{ + int result; + + if (crl == NULL || issuer == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + /* disable all the unneeded OPTIONAL fields. + */ + disable_optional_stuff (crl); + + result = _gnutls_x509_pkix_sign (crl->crl, "tbsCertList", + dig, issuer, issuer_key); + if (result < 0) + { + gnutls_assert (); + return result; + } + + return 0; +} + +/** + * gnutls_x509_crl_sign - This function will sign a CRL with a key + * @crl: should contain a gnutls_x509_crl_t structure + * @issuer: is the certificate of the certificate issuer + * @issuer_key: holds the issuer's private key + * + * This function is the same a gnutls_x509_crl_sign2() with no flags, and + * SHA1 as the hash algorithm. + * + * Returns 0 on success. + * + **/ +int +gnutls_x509_crl_sign (gnutls_x509_crl_t crl, gnutls_x509_crt_t issuer, + gnutls_x509_privkey_t issuer_key) +{ + return gnutls_x509_crl_sign2 (crl, issuer, issuer_key, GNUTLS_DIG_SHA1, 0); +} + +/** + * gnutls_x509_crl_set_this_update - This function will set the CRL's issuing time + * @crl: should contain a gnutls_x509_crl_t structure + * @act_time: The actual time + * + * This function will set the time this CRL was issued. + * + * Returns 0 on success, or a negative value in case of an error. + * + **/ +int +gnutls_x509_crl_set_this_update (gnutls_x509_crl_t crl, time_t act_time) +{ + if (crl == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_set_time (crl->crl, "tbsCertList.thisUpdate", act_time); +} + +/** + * gnutls_x509_crl_set_next_update - This function will set the CRL next update time + * @crl: should contain a gnutls_x509_crl_t structure + * @exp_time: The actual time + * + * This function will set the time this CRL will be updated. + * + * Returns 0 on success, or a negative value in case of an error. + * + **/ +int +gnutls_x509_crl_set_next_update (gnutls_x509_crl_t crl, time_t exp_time) +{ + if (crl == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + return _gnutls_x509_set_time (crl->crl, "tbsCertList.nextUpdate", exp_time); +} + +/** + * gnutls_x509_crl_set_crt_serial - This function will set a revoked certificate's serial number + * @crl: should contain a gnutls_x509_crl_t structure + * @serial: The revoked certificate's serial number + * @serial_size: Holds the size of the serial field. + * @revocation_time: The time this certificate was revoked + * + * This function will set a revoked certificate's serial number to the CRL. + * + * Returns 0 on success, or a negative value in case of an error. + * + **/ +int +gnutls_x509_crl_set_crt_serial (gnutls_x509_crl_t crl, + const void *serial, size_t serial_size, + time_t revocation_time) +{ + int ret; + + if (crl == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = + asn1_write_value (crl->crl, "tbsCertList.revokedCertificates", "NEW", 1); + if (ret != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (ret); + } + + ret = + asn1_write_value (crl->crl, + "tbsCertList.revokedCertificates.?LAST.userCertificate", + serial, serial_size); + if (ret != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (ret); + } + + ret = + _gnutls_x509_set_time (crl->crl, + "tbsCertList.revokedCertificates.?LAST.revocationDate", + revocation_time); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + ret = + asn1_write_value (crl->crl, + "tbsCertList.revokedCertificates.?LAST.crlEntryExtensions", + NULL, 0); + if (ret != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (ret); + } + + return 0; +} + +/** + * gnutls_x509_crl_set_crt - This function will set a revoked certificate's serial number + * @crl: should contain a gnutls_x509_crl_t structure + * @crt: should contain a gnutls_x509_crt_t structure with the revoked certificate + * @revocation_time: The time this certificate was revoked + * + * This function will set a revoked certificate's serial number to the CRL. + * + * Returns 0 on success, or a negative value in case of an error. + * + **/ +int +gnutls_x509_crl_set_crt (gnutls_x509_crl_t crl, gnutls_x509_crt_t crt, + time_t revocation_time) +{ + int ret; + opaque serial[128]; + size_t serial_size; + + if (crl == NULL || crt == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + serial_size = sizeof (serial); + ret = gnutls_x509_crt_get_serial (crt, serial, &serial_size); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + ret = + gnutls_x509_crl_set_crt_serial (crl, serial, serial_size, + revocation_time); + if (ret < 0) + { + gnutls_assert (); + return _gnutls_asn2err (ret); + } + + return 0; +} + + +/* If OPTIONAL fields have not been initialized then + * disable them. + */ +static void +disable_optional_stuff (gnutls_x509_crl_t crl) +{ + + asn1_write_value (crl->crl, "tbsCertList.crlExtensions", NULL, 0); + + return; +} + +#endif /* ENABLE_PKI */ diff --git a/src/daemon/https/x509/crq.c b/src/daemon/https/x509/crq.c new file mode 100644 index 00000000..8e663d51 --- /dev/null +++ b/src/daemon/https/x509/crq.c @@ -0,0 +1,887 @@ +/* + * Copyright (C) 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +/* This file contains functions to handle PKCS #10 certificate requests. + */ + +#include + +#ifdef ENABLE_PKI + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * gnutls_x509_crq_init - This function initializes a gnutls_x509_crq_t structure + * @crq: The structure to be initialized + * + * This function will initialize a PKCS10 certificate request structure. + * + * Returns 0 on success. + * + **/ +int +gnutls_x509_crq_init (gnutls_x509_crq_t * crq) +{ + *crq = gnutls_calloc (1, sizeof (gnutls_x509_crq_int)); + + if (*crq) + { + int result = asn1_create_element (_gnutls_get_pkix (), + "PKIX1.pkcs-10-CertificationRequest", + &((*crq)->crq)); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + gnutls_free (*crq); + return _gnutls_asn2err (result); + } + return 0; /* success */ + } + return GNUTLS_E_MEMORY_ERROR; +} + +/** + * gnutls_x509_crq_deinit - This function deinitializes memory used by a gnutls_x509_crq_t structure + * @crq: The structure to be initialized + * + * This function will deinitialize a CRL structure. + * + **/ +void +gnutls_x509_crq_deinit (gnutls_x509_crq_t crq) +{ + if (!crq) + return; + + if (crq->crq) + asn1_delete_structure (&crq->crq); + + gnutls_free (crq); +} + +#define PEM_CRQ "NEW CERTIFICATE REQUEST" +#define PEM_CRQ2 "CERTIFICATE REQUEST" + +/** + * gnutls_x509_crq_import - This function will import a DER or PEM encoded Certificate request + * @crq: The structure to store the parsed certificate request. + * @data: The DER or PEM encoded certificate. + * @format: One of DER or PEM + * + * This function will convert the given DER or PEM encoded Certificate + * to the native gnutls_x509_crq_t format. The output will be stored in @cert. + * + * If the Certificate is PEM encoded it should have a header of "NEW CERTIFICATE REQUEST". + * + * Returns 0 on success. + * + **/ +int +gnutls_x509_crq_import (gnutls_x509_crq_t crq, + const gnutls_datum_t * data, + gnutls_x509_crt_fmt_t format) +{ + int result = 0, need_free = 0; + gnutls_datum_t _data; + + if (crq == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + _data.data = data->data; + _data.size = data->size; + + /* If the Certificate is in PEM format then decode it + */ + if (format == GNUTLS_X509_FMT_PEM) + { + opaque *out; + + /* Try the first header */ + result = _gnutls_fbase64_decode (PEM_CRQ, data->data, data->size, &out); + + if (result <= 0) /* Go for the second header */ + result = + _gnutls_fbase64_decode (PEM_CRQ2, data->data, data->size, &out); + + if (result <= 0) + { + if (result == 0) + result = GNUTLS_E_INTERNAL_ERROR; + gnutls_assert (); + return result; + } + + _data.data = out; + _data.size = result; + + need_free = 1; + } + + result = asn1_der_decoding (&crq->crq, _data.data, _data.size, NULL); + if (result != ASN1_SUCCESS) + { + result = _gnutls_asn2err (result); + gnutls_assert (); + goto cleanup; + } + + result = 0; + +cleanup: + if (need_free) + _gnutls_free_datum (&_data); + return result; +} + + + +/** + * gnutls_x509_crq_get_dn - This function returns the Certificate request subject's distinguished name + * @crq: should contain a gnutls_x509_crq_t structure + * @buf: a pointer to a structure to hold the name (may be null) + * @sizeof_buf: initially holds the size of @buf + * + * This function will copy the name of the Certificate request + * subject in the provided buffer. The name will be in the form + * "C=xxxx,O=yyyy,CN=zzzz" as described in RFC2253. The output string + * will be ASCII or UTF-8 encoded, depending on the certificate data. + * + * If @buf is null then only the size will be filled. + * + * Returns GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is not + * long enough, and in that case the *sizeof_buf will be updated with + * the required size. On success 0 is returned. + * + **/ +int +gnutls_x509_crq_get_dn (gnutls_x509_crq_t crq, char *buf, size_t * sizeof_buf) +{ + if (crq == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_parse_dn (crq->crq, + "certificationRequestInfo.subject.rdnSequence", + buf, sizeof_buf); +} + +/** + * gnutls_x509_crq_get_dn_by_oid - This function returns the Certificate request subject's distinguished name + * @crq: should contain a gnutls_x509_crq_t structure + * @oid: holds an Object Identified in null terminated string + * @indx: In case multiple same OIDs exist in the RDN, this specifies + * which to send. Use zero to get the first one. + * @raw_flag: If non zero returns the raw DER data of the DN part. + * @buf: a pointer to a structure to hold the name (may be null) + * @sizeof_buf: initially holds the size of @buf + * + * This function will extract the part of the name of the Certificate + * request subject, specified by the given OID. The output will be + * encoded as described in RFC2253. The output string will be ASCII + * or UTF-8 encoded, depending on the certificate data. + * + * Some helper macros with popular OIDs can be found in gnutls/x509.h + * If raw flag is zero, this function will only return known OIDs as + * text. Other OIDs will be DER encoded, as described in RFC2253 -- + * in hex format with a '\#' prefix. You can check about known OIDs + * using gnutls_x509_dn_oid_known(). + * + * If @buf is null then only the size will be filled. + * + * Returns GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is not + * long enough, and in that case the *sizeof_buf will be updated with + * the required size. On success 0 is returned. + * + **/ +int +gnutls_x509_crq_get_dn_by_oid (gnutls_x509_crq_t crq, const char *oid, + int indx, unsigned int raw_flag, + void *buf, size_t * sizeof_buf) +{ + if (crq == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_parse_dn_oid (crq->crq, + "certificationRequestInfo.subject.rdnSequence", + oid, indx, raw_flag, buf, sizeof_buf); +} + +/** + * gnutls_x509_crq_get_dn_oid - This function returns the Certificate request subject's distinguished name OIDs + * @crq: should contain a gnutls_x509_crq_t structure + * @indx: Specifies which DN OID to send. Use zero to get the first one. + * @oid: a pointer to a structure to hold the name (may be null) + * @sizeof_oid: initially holds the size of @oid + * + * This function will extract the requested OID of the name of the + * Certificate request subject, specified by the given index. + * + * If oid is null then only the size will be filled. + * + * Returns GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is not + * long enough, and in that case the *sizeof_oid will be updated with + * the required size. On success 0 is returned. + * + **/ +int +gnutls_x509_crq_get_dn_oid (gnutls_x509_crq_t crq, + int indx, void *oid, size_t * sizeof_oid) +{ + if (crq == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_get_dn_oid (crq->crq, + "certificationRequestInfo.subject.rdnSequence", + indx, oid, sizeof_oid); +} + +/* Parses an Attribute list in the asn1_struct, and searches for the + * given OID. The index indicates the attribute value to be returned. + * + * If raw==0 only printable data are returned, or GNUTLS_E_X509_UNSUPPORTED_ATTRIBUTE. + * + * asn1_attr_name must be a string in the form "certificationRequestInfo.attributes" + * + */ +static int +parse_attribute (ASN1_TYPE asn1_struct, + const char *attr_name, const char *given_oid, int indx, + int raw, char *buf, size_t * sizeof_buf) +{ + int k1, result; + char tmpbuffer1[MAX_NAME_SIZE]; + char tmpbuffer3[MAX_NAME_SIZE]; + char value[200]; + char oid[128]; + int len, printable; + + if (*sizeof_buf == 0) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + buf[0] = 0; + + k1 = 0; + do + { + + k1++; + /* create a string like "attribute.?1" + */ + if (attr_name[0] != 0) + snprintf (tmpbuffer1, sizeof (tmpbuffer1), "%s.?%u", attr_name, k1); + else + snprintf (tmpbuffer1, sizeof (tmpbuffer1), "?%u", k1); + + len = sizeof (value) - 1; + result = asn1_read_value (asn1_struct, tmpbuffer1, value, &len); + + if (result == ASN1_ELEMENT_NOT_FOUND) + { + gnutls_assert (); + break; + } + + if (result != ASN1_VALUE_NOT_FOUND) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + /* Move to the attibute type and values + */ + /* Read the OID + */ + _gnutls_str_cpy (tmpbuffer3, sizeof (tmpbuffer3), tmpbuffer1); + _gnutls_str_cat (tmpbuffer3, sizeof (tmpbuffer3), ".type"); + + len = sizeof (oid) - 1; + result = asn1_read_value (asn1_struct, tmpbuffer3, oid, &len); + + if (result == ASN1_ELEMENT_NOT_FOUND) + break; + else if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + if (strcmp (oid, given_oid) == 0) + { /* Found the OID */ + + /* Read the Value + */ + snprintf (tmpbuffer3, sizeof (tmpbuffer3), "%s.values.?%u", + tmpbuffer1, indx + 1); + + len = sizeof (value) - 1; + result = asn1_read_value (asn1_struct, tmpbuffer3, value, &len); + + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + if (raw == 0) + { + printable = _gnutls_x509_oid_data_printable (oid); + if (printable == 1) + { + if ((result = + _gnutls_x509_oid_data2string + (oid, value, len, buf, sizeof_buf)) < 0) + { + gnutls_assert (); + goto cleanup; + } + return 0; + } + else + { + gnutls_assert (); + return GNUTLS_E_X509_UNSUPPORTED_ATTRIBUTE; + } + } + else + { /* raw!=0 */ + if (*sizeof_buf > (size_t) len && buf != NULL) + { + *sizeof_buf = len; + memcpy (buf, value, len); + + return 0; + } + else + { + *sizeof_buf = len; + gnutls_assert (); + return GNUTLS_E_SHORT_MEMORY_BUFFER; + } + } + } + + } + while (1); + + gnutls_assert (); + + result = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + +cleanup: + return result; +} + +/** + * gnutls_x509_crq_get_challenge_password - This function will get the challenge password + * @crq: should contain a gnutls_x509_crq_t structure + * @pass: will hold a null terminated password + * @sizeof_pass: Initially holds the size of @pass. + * + * This function will return the challenge password in the + * request. + * + * Returns 0 on success. + * + **/ +int +gnutls_x509_crq_get_challenge_password (gnutls_x509_crq_t crq, + char *pass, size_t * sizeof_pass) +{ + if (crq == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + return parse_attribute (crq->crq, "certificationRequestInfo.attributes", + "1.2.840.113549.1.9.7", 0, 0, pass, sizeof_pass); +} + +/** + * gnutls_x509_crq_set_attribute_by_oid - This function will set an attribute in the request + * @crq: should contain a gnutls_x509_crq_t structure + * @oid: holds an Object Identified in null terminated string + * @buf: a pointer to a structure that holds the attribute data + * @sizeof_buf: holds the size of @buf + * + * This function will set the attribute in the certificate request specified + * by the given Object ID. The attribute must be be DER encoded. + * + * Returns 0 on success. + * + **/ +int +gnutls_x509_crq_set_attribute_by_oid (gnutls_x509_crq_t crq, + const char *oid, void *buf, + size_t sizeof_buf) +{ + int result; + + if (crq == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + /* Add the attribute. + */ + result = + asn1_write_value (crq->crq, "certificationRequestInfo.attributes", + "NEW", 1); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + result = + _gnutls_x509_encode_and_write_attribute (oid, + crq->crq, + "certificationRequestInfo.attributes.?LAST", + buf, sizeof_buf, 1); + + if (result < 0) + { + gnutls_assert (); + return result; + } + + return 0; +} + +/** + * gnutls_x509_crq_get_attribute_by_oid - This function will get an attribute of the request + * @crq: should contain a gnutls_x509_crq_t structure + * @oid: holds an Object Identified in null terminated string + * @indx: In case multiple same OIDs exist in the attribute list, this specifies + * which to send. Use zero to get the first one. + * @buf: a pointer to a structure to hold the attribute data (may be null) + * @sizeof_buf: initially holds the size of @buf + * + * This function will return the attribute in the certificate request specified + * by the given Object ID. The attribute will be DER encoded. + * + * Returns 0 on success. + * + **/ +int +gnutls_x509_crq_get_attribute_by_oid (gnutls_x509_crq_t crq, + const char *oid, int indx, void *buf, + size_t * sizeof_buf) +{ + if (crq == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + return parse_attribute (crq->crq, "certificationRequestInfo.attributes", + oid, indx, 1, buf, sizeof_buf); +} + +/** + * gnutls_x509_crq_set_dn_by_oid - This function will set the Certificate request subject's distinguished name + * @crq: should contain a gnutls_x509_crq_t structure + * @oid: holds an Object Identifier in a null terminated string + * @raw_flag: must be 0, or 1 if the data are DER encoded + * @data: a pointer to the input data + * @sizeof_data: holds the size of @data + * + * This function will set the part of the name of the Certificate request subject, specified + * by the given OID. The input string should be ASCII or UTF-8 encoded. + * + * Some helper macros with popular OIDs can be found in gnutls/x509.h + * With this function you can only set the known OIDs. You can test + * for known OIDs using gnutls_x509_dn_oid_known(). For OIDs that are + * not known (by gnutls) you should properly DER encode your data, and + * call this function with raw_flag set. + * + * Returns 0 on success. + * + **/ +int +gnutls_x509_crq_set_dn_by_oid (gnutls_x509_crq_t crq, const char *oid, + unsigned int raw_flag, const void *data, + unsigned int sizeof_data) +{ + if (sizeof_data == 0 || data == NULL || crq == NULL) + { + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_set_dn_oid (crq->crq, + "certificationRequestInfo.subject", oid, + raw_flag, data, sizeof_data); +} + +/** + * gnutls_x509_crq_set_version - This function will set the Certificate request version + * @crq: should contain a gnutls_x509_crq_t structure + * @version: holds the version number. For v1 Requests must be 1. + * + * This function will set the version of the certificate request. For + * version 1 requests this must be one. + * + * Returns 0 on success. + * + **/ +int +gnutls_x509_crq_set_version (gnutls_x509_crq_t crq, unsigned int version) +{ + int result; + unsigned char null = version; + + if (crq == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + if (null > 0) + null--; + + result = + asn1_write_value (crq->crq, "certificationRequestInfo.version", &null, 1); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + return 0; +} + +/** + * gnutls_x509_crq_get_version - This function returns the Certificate request's version number + * @crq: should contain a gnutls_x509_crq_t structure + * + * This function will return the version of the specified Certificate request. + * + * Returns a negative value on error. + * + **/ +int +gnutls_x509_crq_get_version (gnutls_x509_crq_t crq) +{ + opaque version[5]; + int len, result; + + if (crq == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + len = sizeof (version); + if ((result = + asn1_read_value (crq->crq, "certificationRequestInfo.version", + version, &len)) != ASN1_SUCCESS) + { + + if (result == ASN1_ELEMENT_NOT_FOUND) + return 1; /* the DEFAULT version */ + gnutls_assert (); + return _gnutls_asn2err (result); + } + + return (int) version[0] + 1; +} + +/** + * gnutls_x509_crq_set_key - This function will associate the Certificate request with a key + * @crq: should contain a gnutls_x509_crq_t structure + * @key: holds a private key + * + * This function will set the public parameters from the given private key to the + * request. Only RSA keys are currently supported. + * + * Returns 0 on success. + * + **/ +int +gnutls_x509_crq_set_key (gnutls_x509_crq_t crq, gnutls_x509_privkey_t key) +{ + int result; + + if (crq == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + result = _gnutls_x509_encode_and_copy_PKI_params (crq->crq, + "certificationRequestInfo.subjectPKInfo", + key->pk_algorithm, + key->params, + key->params_size); + + if (result < 0) + { + gnutls_assert (); + return result; + } + + return 0; +} + +/** + * gnutls_x509_crq_set_challenge_password - This function will set a challenge password + * @crq: should contain a gnutls_x509_crq_t structure + * @pass: holds a null terminated password + * + * This function will set a challenge password to be used when revoking the request. + * + * Returns 0 on success. + * + **/ +int +gnutls_x509_crq_set_challenge_password (gnutls_x509_crq_t crq, + const char *pass) +{ + int result; + + if (crq == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + /* Add the attribute. + */ + result = + asn1_write_value (crq->crq, "certificationRequestInfo.attributes", + "NEW", 1); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + result = + _gnutls_x509_encode_and_write_attribute ("1.2.840.113549.1.9.7", + crq->crq, + "certificationRequestInfo.attributes.?LAST", + pass, strlen (pass), 1); + + if (result < 0) + { + gnutls_assert (); + return result; + } + + return 0; +} + +/** + * gnutls_x509_crq_sign2 - This function will sign a Certificate request with a key + * @crq: should contain a gnutls_x509_crq_t structure + * @key: holds a private key + * @dig: The message digest to use. GNUTLS_DIG_SHA1 is the safe choice unless you know what you're doing. + * @flags: must be 0 + * + * This function will sign the certificate request with a private key. + * This must be the same key as the one used in gnutls_x509_crt_set_key() since a + * certificate request is self signed. + * + * This must be the last step in a certificate request generation since all + * the previously set parameters are now signed. + * + * Returns 0 on success. + * + **/ +int +gnutls_x509_crq_sign2 (gnutls_x509_crq_t crq, gnutls_x509_privkey_t key, + gnutls_digest_algorithm_t dig, unsigned int flags) +{ + int result; + gnutls_datum_t signature; + + if (crq == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + /* Step 1. Self sign the request. + */ + result = + _gnutls_x509_sign_tbs (crq->crq, "certificationRequestInfo", + dig, key, &signature); + + if (result < 0) + { + gnutls_assert (); + return result; + } + + /* Step 2. write the signature (bits) + */ + result = + asn1_write_value (crq->crq, "signature", signature.data, + signature.size * 8); + + _gnutls_free_datum (&signature); + + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + /* Step 3. Write the signatureAlgorithm field. + */ + result = _gnutls_x509_write_sig_params (crq->crq, "signatureAlgorithm", + key->pk_algorithm, dig, key->params, + key->params_size); + if (result < 0) + { + gnutls_assert (); + return result; + } + + return 0; +} + +/** + * gnutls_x509_crq_sign - This function will sign a Certificate request with a key + * @crq: should contain a gnutls_x509_crq_t structure + * @key: holds a private key + * + * This function is the same a gnutls_x509_crq_sign2() with no flags, and + * SHA1 as the hash algorithm. + * + * Returns 0 on success. + * + **/ +int +gnutls_x509_crq_sign (gnutls_x509_crq_t crq, gnutls_x509_privkey_t key) +{ + return gnutls_x509_crq_sign2 (crq, key, GNUTLS_DIG_SHA1, 0); +} + +/** + * gnutls_x509_crq_export - Export the generated certificate request + * @crq: Holds the request + * @format: the format of output params. One of PEM or DER. + * @output_data: will contain a certificate request PEM or DER encoded + * @output_data_size: holds the size of output_data (and will be + * replaced by the actual size of parameters) + * + * This function will export the certificate request to a PKCS10 + * + * If the buffer provided is not long enough to hold the output, then + * GNUTLS_E_SHORT_MEMORY_BUFFER will be returned and + * *output_data_size will be updated. + * + * If the structure is PEM encoded, it will have a header of "BEGIN + * NEW CERTIFICATE REQUEST". + * + * Return value: In case of failure a negative value will be + * returned, and 0 on success. + * + **/ +int +gnutls_x509_crq_export (gnutls_x509_crq_t crq, + gnutls_x509_crt_fmt_t format, void *output_data, + size_t * output_data_size) +{ + if (crq == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_export_int (crq->crq, format, PEM_CRQ, + output_data, output_data_size); +} + +/** + * gnutls_x509_crq_get_pk_algorithm - This function returns the certificate request's PublicKey algorithm + * @crq: should contain a gnutls_x509_crq_t structure + * @bits: if bits is non null it will hold the size of the parameters' in bits + * + * This function will return the public key algorithm of a PKCS \#10 + * certificate request. + * + * If bits is non null, it should have enough size to hold the parameters + * size in bits. For RSA the bits returned is the modulus. + * For DSA the bits returned are of the public + * exponent. + * + * Returns a member of the gnutls_pk_algorithm_t enumeration on success, + * or a negative value on error. + * + **/ +int +gnutls_x509_crq_get_pk_algorithm (gnutls_x509_crq_t crq, unsigned int *bits) +{ + int result; + + if (crq == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + result = + _gnutls_x509_get_pk_algorithm (crq->crq, + "certificationRequestInfo.subjectPKInfo", + bits); + if (result < 0) + { + gnutls_assert (); + } + + return result; +} + +#endif /* ENABLE_PKI */ diff --git a/src/daemon/https/x509/crq.h b/src/daemon/https/x509/crq.h new file mode 100644 index 00000000..80e600b5 --- /dev/null +++ b/src/daemon/https/x509/crq.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +#include + +typedef struct gnutls_x509_crq_int +{ + ASN1_TYPE crq; +} gnutls_x509_crq_int; diff --git a/src/daemon/https/x509/dn.c b/src/daemon/https/x509/dn.c new file mode 100644 index 00000000..c356aac1 --- /dev/null +++ b/src/daemon/https/x509/dn.c @@ -0,0 +1,1140 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2007 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* This file includes all the required to parse an X.509 Distriguished + * Name (you need a parser just to read a name in the X.509 protoocols!!!) + */ + +/* Converts the given OID to an ldap acceptable string or + * a dotted OID. + */ +static const char * +oid2ldap_string (const char *oid) +{ + const char *ret; + + ret = _gnutls_x509_oid2ldap_string (oid); + if (ret) + return ret; + + /* else return the OID in dotted format */ + return oid; +} + +/* Escapes a string following the rules from RFC2253. + */ +static char * +str_escape (char *str, char *buffer, unsigned int buffer_size) +{ + int str_length, j, i; + + if (str == NULL || buffer == NULL) + return NULL; + + str_length = MIN (strlen (str), buffer_size - 1); + + for (i = j = 0; i < str_length; i++) + { + if (str[i] == ',' || str[i] == '+' || str[i] == '"' + || str[i] == '\\' || str[i] == '<' || str[i] == '>' + || str[i] == ';') + buffer[j++] = '\\'; + + buffer[j++] = str[i]; + } + + /* null terminate the string */ + buffer[j] = 0; + + return buffer; +} + +/* Parses an X509 DN in the asn1_struct, and puts the output into + * the string buf. The output is an LDAP encoded DN. + * + * asn1_rdn_name must be a string in the form "tbsCertificate.issuer.rdnSequence". + * That is to point in the rndSequence. + */ +int +_gnutls_x509_parse_dn (ASN1_TYPE asn1_struct, + const char *asn1_rdn_name, char *buf, + size_t * sizeof_buf) +{ + gnutls_string out_str; + int k2, k1, result; + char tmpbuffer1[MAX_NAME_SIZE]; + char tmpbuffer2[MAX_NAME_SIZE]; + char tmpbuffer3[MAX_NAME_SIZE]; + opaque value[MAX_STRING_LEN], *value2 = NULL; + char *escaped = NULL; + const char *ldap_desc; + char oid[128]; + int len, printable; + char *string = NULL; + size_t sizeof_string, sizeof_escaped; + + if (sizeof_buf == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + if (*sizeof_buf > 0 && buf) + buf[0] = 0; + else + *sizeof_buf = 0; + + _gnutls_string_init (&out_str, gnutls_malloc, gnutls_realloc, gnutls_free); + + k1 = 0; + do + { + + k1++; + /* create a string like "tbsCertList.issuer.rdnSequence.?1" + */ + if (asn1_rdn_name[0] != 0) + snprintf (tmpbuffer1, sizeof (tmpbuffer1), "%s.?%u", asn1_rdn_name, + k1); + else + snprintf (tmpbuffer1, sizeof (tmpbuffer1), "?%u", k1); + + len = sizeof (value) - 1; + result = asn1_read_value (asn1_struct, tmpbuffer1, value, &len); + + if (result == ASN1_ELEMENT_NOT_FOUND) + { + break; + } + + if (result != ASN1_VALUE_NOT_FOUND) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + k2 = 0; + + do + { /* Move to the attibute type and values + */ + k2++; + + if (tmpbuffer1[0] != 0) + snprintf (tmpbuffer2, sizeof (tmpbuffer2), "%s.?%u", tmpbuffer1, + k2); + else + snprintf (tmpbuffer2, sizeof (tmpbuffer2), "?%u", k2); + + /* Try to read the RelativeDistinguishedName attributes. + */ + + len = sizeof (value) - 1; + result = asn1_read_value (asn1_struct, tmpbuffer2, value, &len); + + if (result == ASN1_ELEMENT_NOT_FOUND) + break; + if (result != ASN1_VALUE_NOT_FOUND) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + /* Read the OID + */ + _gnutls_str_cpy (tmpbuffer3, sizeof (tmpbuffer3), tmpbuffer2); + _gnutls_str_cat (tmpbuffer3, sizeof (tmpbuffer3), ".type"); + + len = sizeof (oid) - 1; + result = asn1_read_value (asn1_struct, tmpbuffer3, oid, &len); + + if (result == ASN1_ELEMENT_NOT_FOUND) + break; + else if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + /* Read the Value + */ + _gnutls_str_cpy (tmpbuffer3, sizeof (tmpbuffer3), tmpbuffer2); + _gnutls_str_cat (tmpbuffer3, sizeof (tmpbuffer3), ".value"); + + len = 0; + result = asn1_read_value (asn1_struct, tmpbuffer3, NULL, &len); + + value2 = gnutls_malloc (len); + if (value2 == NULL) + { + gnutls_assert (); + result = GNUTLS_E_MEMORY_ERROR; + goto cleanup; + } + + result = asn1_read_value (asn1_struct, tmpbuffer3, value2, &len); + + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } +#define STR_APPEND(y) if ((result=_gnutls_string_append_str( &out_str, y)) < 0) { \ + gnutls_assert(); \ + goto cleanup; \ +} + /* The encodings of adjoining RelativeDistinguishedNames are separated + * by a comma character (',' ASCII 44). + */ + + /* Where there is a multi-valued RDN, the outputs from adjoining + * AttributeTypeAndValues are separated by a plus ('+' ASCII 43) + * character. + */ + if (k1 != 1) + { /* the first time do not append a comma */ + if (k2 != 1) + { /* adjoining multi-value RDN */ + STR_APPEND ("+"); + } + else + { + STR_APPEND (","); + } + } + + ldap_desc = oid2ldap_string (oid); + printable = _gnutls_x509_oid_data_printable (oid); + + sizeof_escaped = 2 * len + 1; + + escaped = gnutls_malloc (sizeof_escaped); + if (escaped == NULL) + { + gnutls_assert (); + result = GNUTLS_E_MEMORY_ERROR; + goto cleanup; + } + + sizeof_string = 2 * len + 2; /* in case it is not printable */ + + string = gnutls_malloc (sizeof_string); + if (string == NULL) + { + gnutls_assert (); + result = GNUTLS_E_MEMORY_ERROR; + goto cleanup; + } + + STR_APPEND (ldap_desc); + STR_APPEND ("="); + result = 0; + + if (printable) + result = + _gnutls_x509_oid_data2string (oid, + value2, len, + string, &sizeof_string); + + if (!printable || result < 0) + result = + _gnutls_x509_data2hex (value2, len, string, &sizeof_string); + + if (result < 0) + { + gnutls_assert (); + _gnutls_x509_log + ("Found OID: '%s' with value '%s'\n", + oid, _gnutls_bin2hex (value2, len, escaped, sizeof_escaped)); + goto cleanup; + } + STR_APPEND (str_escape (string, escaped, sizeof_escaped)); + gnutls_free (string); + string = NULL; + + gnutls_free (escaped); + escaped = NULL; + gnutls_free (value2); + value2 = NULL; + + } + while (1); + + } + while (1); + + if (out_str.length >= (unsigned int) *sizeof_buf) + { + gnutls_assert (); + *sizeof_buf = out_str.length + 1; + result = GNUTLS_E_SHORT_MEMORY_BUFFER; + goto cleanup; + } + + if (buf) + { + memcpy (buf, out_str.data, out_str.length); + buf[out_str.length] = 0; + } + *sizeof_buf = out_str.length; + + result = 0; + +cleanup: + gnutls_free (value2); + gnutls_free (string); + gnutls_free (escaped); + _gnutls_string_clear (&out_str); + return result; +} + +/* Parses an X509 DN in the asn1_struct, and searches for the + * given OID in the DN. + * + * If raw_flag == 0, the output will be encoded in the LDAP way. (#hex for non printable) + * Otherwise the raw DER data are returned. + * + * asn1_rdn_name must be a string in the form "tbsCertificate.issuer.rdnSequence". + * That is to point in the rndSequence. + * + * indx specifies which OID to return. Ie 0 means return the first specified + * OID found, 1 the second etc. + */ +int +_gnutls_x509_parse_dn_oid (ASN1_TYPE asn1_struct, + const char *asn1_rdn_name, + const char *given_oid, int indx, + unsigned int raw_flag, + void *buf, size_t * sizeof_buf) +{ + int k2, k1, result; + char tmpbuffer1[MAX_NAME_SIZE]; + char tmpbuffer2[MAX_NAME_SIZE]; + char tmpbuffer3[MAX_NAME_SIZE]; + opaque value[256]; + char oid[128]; + int len, printable; + int i = 0; + char *cbuf = buf; + + if (cbuf == NULL) + *sizeof_buf = 0; + else + cbuf[0] = 0; + + k1 = 0; + do + { + + k1++; + /* create a string like "tbsCertList.issuer.rdnSequence.?1" + */ + if (asn1_rdn_name[0] != 0) + snprintf (tmpbuffer1, sizeof (tmpbuffer1), "%s.?%u", asn1_rdn_name, + k1); + else + snprintf (tmpbuffer1, sizeof (tmpbuffer1), "?%u", k1); + + len = sizeof (value) - 1; + result = asn1_read_value (asn1_struct, tmpbuffer1, value, &len); + + if (result == ASN1_ELEMENT_NOT_FOUND) + { + gnutls_assert (); + break; + } + + if (result != ASN1_VALUE_NOT_FOUND) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + k2 = 0; + + do + { /* Move to the attibute type and values + */ + k2++; + + if (tmpbuffer1[0] != 0) + snprintf (tmpbuffer2, sizeof (tmpbuffer2), "%s.?%u", tmpbuffer1, + k2); + else + snprintf (tmpbuffer2, sizeof (tmpbuffer2), "?%u", k2); + + /* Try to read the RelativeDistinguishedName attributes. + */ + + len = sizeof (value) - 1; + result = asn1_read_value (asn1_struct, tmpbuffer2, value, &len); + + if (result == ASN1_ELEMENT_NOT_FOUND) + { + break; + } + if (result != ASN1_VALUE_NOT_FOUND) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + /* Read the OID + */ + _gnutls_str_cpy (tmpbuffer3, sizeof (tmpbuffer3), tmpbuffer2); + _gnutls_str_cat (tmpbuffer3, sizeof (tmpbuffer3), ".type"); + + len = sizeof (oid) - 1; + result = asn1_read_value (asn1_struct, tmpbuffer3, oid, &len); + + if (result == ASN1_ELEMENT_NOT_FOUND) + break; + else if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + if (strcmp (oid, given_oid) == 0 && indx == i++) + { /* Found the OID */ + + /* Read the Value + */ + _gnutls_str_cpy (tmpbuffer3, sizeof (tmpbuffer3), tmpbuffer2); + _gnutls_str_cat (tmpbuffer3, sizeof (tmpbuffer3), ".value"); + + len = *sizeof_buf; + result = asn1_read_value (asn1_struct, tmpbuffer3, buf, &len); + + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + if (result == ASN1_MEM_ERROR) + *sizeof_buf = len; + result = _gnutls_asn2err (result); + goto cleanup; + } + + if (raw_flag != 0) + { + if ((unsigned) len > *sizeof_buf) + { + *sizeof_buf = len; + result = GNUTLS_E_SHORT_MEMORY_BUFFER; + goto cleanup; + } + *sizeof_buf = len; + + return 0; + + } + else + { /* parse data. raw_flag == 0 */ + printable = _gnutls_x509_oid_data_printable (oid); + + if (printable == 1) + result = + _gnutls_x509_oid_data2string (oid, buf, len, + cbuf, sizeof_buf); + else + result = + _gnutls_x509_data2hex (buf, len, cbuf, sizeof_buf); + + if (result < 0) + { + gnutls_assert (); + goto cleanup; + } + + return 0; + + } /* raw_flag == 0 */ + } + } + while (1); + + } + while (1); + + gnutls_assert (); + + result = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + +cleanup: + return result; +} + + +/* Parses an X509 DN in the asn1_struct, and returns the requested + * DN OID. + * + * asn1_rdn_name must be a string in the form "tbsCertificate.issuer.rdnSequence". + * That is to point in the rndSequence. + * + * indx specifies which OID to return. Ie 0 means return the first specified + * OID found, 1 the second etc. + */ +int +_gnutls_x509_get_dn_oid (ASN1_TYPE asn1_struct, + const char *asn1_rdn_name, + int indx, void *_oid, size_t * sizeof_oid) +{ + int k2, k1, result; + char tmpbuffer1[MAX_NAME_SIZE]; + char tmpbuffer2[MAX_NAME_SIZE]; + char tmpbuffer3[MAX_NAME_SIZE]; + char value[256]; + char oid[128]; + int len; + int i = 0; + + k1 = 0; + do + { + + k1++; + /* create a string like "tbsCertList.issuer.rdnSequence.?1" + */ + if (asn1_rdn_name[0] != 0) + snprintf (tmpbuffer1, sizeof (tmpbuffer1), "%s.?%u", asn1_rdn_name, + k1); + else + snprintf (tmpbuffer1, sizeof (tmpbuffer1), "?%u", k1); + + len = sizeof (value) - 1; + result = asn1_read_value (asn1_struct, tmpbuffer1, value, &len); + + if (result == ASN1_ELEMENT_NOT_FOUND) + { + gnutls_assert (); + break; + } + + if (result != ASN1_VALUE_NOT_FOUND) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + k2 = 0; + + do + { /* Move to the attibute type and values + */ + k2++; + + if (tmpbuffer1[0] != 0) + snprintf (tmpbuffer2, sizeof (tmpbuffer2), "%s.?%u", tmpbuffer1, + k2); + else + snprintf (tmpbuffer2, sizeof (tmpbuffer2), "?%u", k2); + + /* Try to read the RelativeDistinguishedName attributes. + */ + + len = sizeof (value) - 1; + result = asn1_read_value (asn1_struct, tmpbuffer2, value, &len); + + if (result == ASN1_ELEMENT_NOT_FOUND) + { + break; + } + if (result != ASN1_VALUE_NOT_FOUND) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + /* Read the OID + */ + _gnutls_str_cpy (tmpbuffer3, sizeof (tmpbuffer3), tmpbuffer2); + _gnutls_str_cat (tmpbuffer3, sizeof (tmpbuffer3), ".type"); + + len = sizeof (oid) - 1; + result = asn1_read_value (asn1_struct, tmpbuffer3, oid, &len); + + if (result == ASN1_ELEMENT_NOT_FOUND) + break; + else if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + if (indx == i++) + { /* Found the OID */ + + len = strlen (oid) + 1; + + if (*sizeof_oid < (unsigned) len) + { + *sizeof_oid = len; + gnutls_assert (); + return GNUTLS_E_SHORT_MEMORY_BUFFER; + } + + memcpy (_oid, oid, len); + *sizeof_oid = len - 1; + + return 0; + } + } + while (1); + + } + while (1); + + gnutls_assert (); + + result = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + +cleanup: + return result; +} + +/* This will encode and write the AttributeTypeAndValue field. + * 'multi' must be zero if writing an AttributeTypeAndValue, and 1 if Attribute. + * In all cases only one value is written. + */ +int +_gnutls_x509_encode_and_write_attribute (const char *given_oid, + ASN1_TYPE asn1_struct, + const char *where, + const void *_data, + int sizeof_data, int multi) +{ + const char *val_name; + const opaque *data = _data; + char tmp[128]; + ASN1_TYPE c2; + int result; + + + /* Find how to encode the data. + */ + val_name = asn1_find_structure_from_oid (_gnutls_get_pkix (), given_oid); + if (val_name == NULL) + { + gnutls_assert (); + return GNUTLS_E_X509_UNSUPPORTED_OID; + } + + _gnutls_str_cpy (tmp, sizeof (tmp), "PKIX1."); + _gnutls_str_cat (tmp, sizeof (tmp), val_name); + + result = asn1_create_element (_gnutls_get_pkix (), tmp, &c2); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + tmp[0] = 0; + + if ((result = _gnutls_x509_oid_data_choice (given_oid)) > 0) + { + char *string_type; + int i; + + string_type = "printableString"; + + /* Check if the data is plain ascii, and use + * the UTF8 string type if not. + */ + for (i = 0; i < sizeof_data; i++) + { + if (!isascii (data[i])) + { + string_type = "utf8String"; + break; + } + } + + /* if the type is a CHOICE then write the + * type we'll use. + */ + result = asn1_write_value (c2, "", string_type, 1); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + asn1_delete_structure (&c2); + return _gnutls_asn2err (result); + } + + _gnutls_str_cpy (tmp, sizeof (tmp), string_type); + } + + result = asn1_write_value (c2, tmp, data, sizeof_data); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + asn1_delete_structure (&c2); + return _gnutls_asn2err (result); + } + + + /* write the data (value) + */ + + _gnutls_str_cpy (tmp, sizeof (tmp), where); + _gnutls_str_cat (tmp, sizeof (tmp), ".value"); + + if (multi != 0) + { /* if not writing an AttributeTypeAndValue, but an Attribute */ + _gnutls_str_cat (tmp, sizeof (tmp), "s"); /* values */ + + result = asn1_write_value (asn1_struct, tmp, "NEW", 1); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + _gnutls_str_cat (tmp, sizeof (tmp), ".?LAST"); + + } + + result = _gnutls_x509_der_encode_and_copy (c2, "", asn1_struct, tmp, 0); + if (result < 0) + { + gnutls_assert (); + return result; + } + + /* write the type + */ + _gnutls_str_cpy (tmp, sizeof (tmp), where); + _gnutls_str_cat (tmp, sizeof (tmp), ".type"); + + result = asn1_write_value (asn1_struct, tmp, given_oid, 1); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + return 0; +} + +/* This will write the AttributeTypeAndValue field. The data must be already DER encoded. + * 'multi' must be zero if writing an AttributeTypeAndValue, and 1 if Attribute. + * In all cases only one value is written. + */ +int +_gnutls_x509_write_attribute (const char *given_oid, + ASN1_TYPE asn1_struct, const char *where, + const void *_data, int sizeof_data, int multi) +{ + char tmp[128]; + int result; + + /* write the data (value) + */ + + _gnutls_str_cpy (tmp, sizeof (tmp), where); + _gnutls_str_cat (tmp, sizeof (tmp), ".value"); + + if (multi != 0) + { /* if not writing an AttributeTypeAndValue, but an Attribute */ + _gnutls_str_cat (tmp, sizeof (tmp), "s"); /* values */ + + result = asn1_write_value (asn1_struct, tmp, "NEW", 1); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + _gnutls_str_cat (tmp, sizeof (tmp), ".?LAST"); + + } + + result = asn1_write_value (asn1_struct, tmp, _data, sizeof_data); + if (result < 0) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + /* write the type + */ + _gnutls_str_cpy (tmp, sizeof (tmp), where); + _gnutls_str_cat (tmp, sizeof (tmp), ".type"); + + result = asn1_write_value (asn1_struct, tmp, given_oid, 1); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + return 0; +} + + +/* Decodes an X.509 Attribute (if multi==1) or an AttributeTypeAndValue + * otherwise. + * + * octet_string should be non zero if we are to decode octet strings after + * decoding. + * + * The output is allocated and stored in value. + */ +int +_gnutls_x509_decode_and_read_attribute (ASN1_TYPE asn1_struct, + const char *where, char *oid, + int oid_size, gnutls_datum_t * value, + int multi, int octet_string) +{ + char tmpbuffer[128]; + int len, result; + + /* Read the OID + */ + _gnutls_str_cpy (tmpbuffer, sizeof (tmpbuffer), where); + _gnutls_str_cat (tmpbuffer, sizeof (tmpbuffer), ".type"); + + len = oid_size - 1; + result = asn1_read_value (asn1_struct, tmpbuffer, oid, &len); + + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + return result; + } + + /* Read the Value + */ + + _gnutls_str_cpy (tmpbuffer, sizeof (tmpbuffer), where); + _gnutls_str_cat (tmpbuffer, sizeof (tmpbuffer), ".value"); + + if (multi) + _gnutls_str_cat (tmpbuffer, sizeof (tmpbuffer), "s.?1"); /* .values.?1 */ + + result = + _gnutls_x509_read_value (asn1_struct, tmpbuffer, value, octet_string); + if (result < 0) + { + gnutls_assert (); + return result; + } + + return 0; + +} + +/* Sets an X509 DN in the asn1_struct, and puts the given OID in the DN. + * The input is assumed to be raw data. + * + * asn1_rdn_name must be a string in the form "tbsCertificate.issuer". + * That is to point before the rndSequence. + * + */ +int +_gnutls_x509_set_dn_oid (ASN1_TYPE asn1_struct, + const char *asn1_name, const char *given_oid, + int raw_flag, const char *name, int sizeof_name) +{ + int result; + char tmp[MAX_NAME_SIZE], asn1_rdn_name[MAX_NAME_SIZE]; + + if (sizeof_name == 0 || name == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + /* create the rdnSequence + */ + result = asn1_write_value (asn1_struct, asn1_name, "rdnSequence", 1); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + _gnutls_str_cpy (asn1_rdn_name, sizeof (asn1_rdn_name), asn1_name); + _gnutls_str_cat (asn1_rdn_name, sizeof (asn1_rdn_name), ".rdnSequence"); + + /* create a new element + */ + result = asn1_write_value (asn1_struct, asn1_rdn_name, "NEW", 1); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + _gnutls_str_cpy (tmp, sizeof (tmp), asn1_rdn_name); + _gnutls_str_cat (tmp, sizeof (tmp), ".?LAST"); + + /* create the set with only one element + */ + result = asn1_write_value (asn1_struct, tmp, "NEW", 1); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + + /* Encode and write the data + */ + _gnutls_str_cpy (tmp, sizeof (tmp), asn1_rdn_name); + _gnutls_str_cat (tmp, sizeof (tmp), ".?LAST.?LAST"); + + if (!raw_flag) + { + result = + _gnutls_x509_encode_and_write_attribute (given_oid, + asn1_struct, + tmp, name, sizeof_name, 0); + } + else + { + result = + _gnutls_x509_write_attribute (given_oid, asn1_struct, + tmp, name, sizeof_name, 0); + } + + if (result < 0) + { + gnutls_assert (); + return result; + } + + return 0; +} + + +/** + * gnutls_x509_rdn_get - This function parses an RDN sequence and returns a string + * @idn: should contain a DER encoded RDN sequence + * @buf: a pointer to a structure to hold the peer's name + * @sizeof_buf: holds the size of @buf + * + * This function will return the name of the given RDN sequence. The + * name will be in the form "C=xxxx,O=yyyy,CN=zzzz" as described in + * RFC2253. + * + * If the provided buffer is not long enough, returns + * GNUTLS_E_SHORT_MEMORY_BUFFER and *sizeof_buf will be updated. On + * success 0 is returned. + * + **/ +int +gnutls_x509_rdn_get (const gnutls_datum_t * idn, + char *buf, size_t * sizeof_buf) +{ + int result; + ASN1_TYPE dn = ASN1_TYPE_EMPTY; + + if (sizeof_buf == 0) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + if (buf) + buf[0] = 0; + + + if ((result = + asn1_create_element (_gnutls_get_pkix (), + "PKIX1.Name", &dn)) != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + result = asn1_der_decoding (&dn, idn->data, idn->size, NULL); + if (result != ASN1_SUCCESS) + { + /* couldn't decode DER */ + gnutls_assert (); + asn1_delete_structure (&dn); + return _gnutls_asn2err (result); + } + + result = _gnutls_x509_parse_dn (dn, "rdnSequence", buf, sizeof_buf); + + asn1_delete_structure (&dn); + return result; + +} + +/** + * gnutls_x509_rdn_get_by_oid - This function parses an RDN sequence and returns a string + * @idn: should contain a DER encoded RDN sequence + * @oid: an Object Identifier + * @indx: In case multiple same OIDs exist in the RDN indicates which + * to send. Use 0 for the first one. + * @raw_flag: If non zero then the raw DER data are returned. + * @buf: a pointer to a structure to hold the peer's name + * @sizeof_buf: holds the size of @buf + * + * This function will return the name of the given Object identifier, + * of the RDN sequence. The name will be encoded using the rules + * from RFC2253. + * + * Returns GNUTLS_E_SHORT_MEMORY_BUFFER and updates *sizeof_buf if + * the provided buffer is not long enough, and 0 on success. + * + **/ +int +gnutls_x509_rdn_get_by_oid (const gnutls_datum_t * idn, const char *oid, + int indx, unsigned int raw_flag, + void *buf, size_t * sizeof_buf) +{ + int result; + ASN1_TYPE dn = ASN1_TYPE_EMPTY; + + if (sizeof_buf == 0) + { + return GNUTLS_E_INVALID_REQUEST; + } + + if ((result = + asn1_create_element (_gnutls_get_pkix (), + "PKIX1.Name", &dn)) != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + result = asn1_der_decoding (&dn, idn->data, idn->size, NULL); + if (result != ASN1_SUCCESS) + { + /* couldn't decode DER */ + gnutls_assert (); + asn1_delete_structure (&dn); + return _gnutls_asn2err (result); + } + + result = + _gnutls_x509_parse_dn_oid (dn, "rdnSequence", oid, indx, + raw_flag, buf, sizeof_buf); + + asn1_delete_structure (&dn); + return result; + +} + +/** + * gnutls_x509_rdn_get_oid - This function parses an RDN sequence and returns an OID. + * @idn: should contain a DER encoded RDN sequence + * @indx: Indicates which OID to return. Use 0 for the first one. + * @oid: a pointer to a structure to hold the peer's name OID + * @sizeof_oid: holds the size of @oid + * + * This function will return the specified Object identifier, of the + * RDN sequence. + * + * Returns GNUTLS_E_SHORT_MEMORY_BUFFER and updates *sizeof_buf if + * the provided buffer is not long enough, and 0 on success. + * + **/ +int +gnutls_x509_rdn_get_oid (const gnutls_datum_t * idn, + int indx, void *buf, size_t * sizeof_buf) +{ + int result; + ASN1_TYPE dn = ASN1_TYPE_EMPTY; + + if (sizeof_buf == 0) + { + return GNUTLS_E_INVALID_REQUEST; + } + + if ((result = + asn1_create_element (_gnutls_get_pkix (), + "PKIX1.Name", &dn)) != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + result = asn1_der_decoding (&dn, idn->data, idn->size, NULL); + if (result != ASN1_SUCCESS) + { + /* couldn't decode DER */ + gnutls_assert (); + asn1_delete_structure (&dn); + return _gnutls_asn2err (result); + } + + result = _gnutls_x509_get_dn_oid (dn, "rdnSequence", indx, buf, sizeof_buf); + + asn1_delete_structure (&dn); + return result; + +} + +/* + * Compares the DER encoded part of a DN. + * + * FIXME: use a real DN comparison algorithm. + * + * Returns 1 if the DN's match and zero if they don't match. Otherwise + * a negative value is returned to indicate error. + */ +int +_gnutls_x509_compare_raw_dn (const gnutls_datum_t * dn1, + const gnutls_datum_t * dn2) +{ + + if (dn1->size != dn2->size) + { + gnutls_assert (); + return 0; + } + if (memcmp (dn1->data, dn2->data, dn2->size) != 0) + { + gnutls_assert (); + return 0; + } + return 1; /* they match */ +} diff --git a/src/daemon/https/x509/dn.h b/src/daemon/https/x509/dn.h new file mode 100644 index 00000000..93a9262c --- /dev/null +++ b/src/daemon/https/x509/dn.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +#ifndef DN_H +# define DN_H + +/* Some OIDs usually found in Distinguished names + */ +#define OID_X520_COUNTRY_NAME "2.5.4.6" +#define OID_X520_ORGANIZATION_NAME "2.5.4.10" +#define OID_X520_ORGANIZATIONAL_UNIT_NAME "2.5.4.11" +#define OID_X520_COMMON_NAME "2.5.4.3" +#define OID_X520_LOCALITY_NAME "2.5.4.7" +#define OID_X520_STATE_OR_PROVINCE_NAME "2.5.4.8" +#define OID_LDAP_DC "0.9.2342.19200300.100.1.25" +#define OID_LDAP_UID "0.9.2342.19200300.100.1.1" +#define OID_PKCS9_EMAIL "1.2.840.113549.1.9.1" + +int _gnutls_x509_parse_dn (ASN1_TYPE asn1_struct, + const char *asn1_rdn_name, char *buf, + size_t * sizeof_buf); + +int _gnutls_x509_parse_dn_oid (ASN1_TYPE asn1_struct, + const char *asn1_rdn_name, const char *oid, + int indx, unsigned int raw_flag, void *buf, + size_t * sizeof_buf); + +int _gnutls_x509_set_dn_oid (ASN1_TYPE asn1_struct, + const char *asn1_rdn_name, const char *oid, + int raw_flag, const char *name, int sizeof_name); + +int _gnutls_x509_get_dn_oid (ASN1_TYPE asn1_struct, + const char *asn1_rdn_name, + int indx, void *_oid, size_t * sizeof_oid); + + +#endif diff --git a/src/daemon/https/x509/dsa.c b/src/daemon/https/x509/dsa.c new file mode 100644 index 00000000..af403911 --- /dev/null +++ b/src/daemon/https/x509/dsa.c @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +/* This file contains code for DSA keys. + */ + +#include +#include +#include +#include + +/* resarr will contain: p(0), q(1), g(2), y(3), x(4). + */ +int +_gnutls_dsa_generate_params (mpi_t * resarr, int *resarr_len, int bits) +{ + + int ret; + gcry_sexp_t parms, key, list; + + /* FIXME: Remove me once we depend on 1.3.1 */ + if (bits > 1024 && gcry_check_version ("1.3.1") == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + if (bits < 512) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = gcry_sexp_build (&parms, NULL, "(genkey(dsa(nbits %d)))", bits); + if (ret != 0) + { + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + /* generate the DSA key + */ + ret = gcry_pk_genkey (&key, parms); + gcry_sexp_release (parms); + + if (ret != 0) + { + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + list = gcry_sexp_find_token (key, "p", 0); + if (list == NULL) + { + gnutls_assert (); + gcry_sexp_release (key); + return GNUTLS_E_INTERNAL_ERROR; + } + + resarr[0] = gcry_sexp_nth_mpi (list, 1, 0); + gcry_sexp_release (list); + + list = gcry_sexp_find_token (key, "q", 0); + if (list == NULL) + { + gnutls_assert (); + gcry_sexp_release (key); + return GNUTLS_E_INTERNAL_ERROR; + } + + resarr[1] = gcry_sexp_nth_mpi (list, 1, 0); + gcry_sexp_release (list); + + list = gcry_sexp_find_token (key, "g", 0); + if (list == NULL) + { + gnutls_assert (); + gcry_sexp_release (key); + return GNUTLS_E_INTERNAL_ERROR; + } + + resarr[2] = gcry_sexp_nth_mpi (list, 1, 0); + gcry_sexp_release (list); + + list = gcry_sexp_find_token (key, "y", 0); + if (list == NULL) + { + gnutls_assert (); + gcry_sexp_release (key); + return GNUTLS_E_INTERNAL_ERROR; + } + + resarr[3] = gcry_sexp_nth_mpi (list, 1, 0); + gcry_sexp_release (list); + + + list = gcry_sexp_find_token (key, "x", 0); + if (list == NULL) + { + gnutls_assert (); + gcry_sexp_release (key); + return GNUTLS_E_INTERNAL_ERROR; + } + + resarr[4] = gcry_sexp_nth_mpi (list, 1, 0); + gcry_sexp_release (list); + + + gcry_sexp_release (key); + + _gnutls_dump_mpi ("p: ", resarr[0]); + _gnutls_dump_mpi ("q: ", resarr[1]); + _gnutls_dump_mpi ("g: ", resarr[2]); + _gnutls_dump_mpi ("y: ", resarr[3]); + _gnutls_dump_mpi ("x: ", resarr[4]); + + *resarr_len = 5; + + return 0; + +} diff --git a/src/daemon/https/x509/dsa.h b/src/daemon/https/x509/dsa.h new file mode 100644 index 00000000..5489ffa3 --- /dev/null +++ b/src/daemon/https/x509/dsa.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +int _gnutls_dsa_generate_params (mpi_t * resarr, int *resarr_len, int bits); diff --git a/src/daemon/https/x509/extensions.c b/src/daemon/https/x509/extensions.c new file mode 100644 index 00000000..98379add --- /dev/null +++ b/src/daemon/https/x509/extensions.c @@ -0,0 +1,1095 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2007 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +/* Functions that relate to the X.509 extension parsing. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* This function will attempt to return the requested extension found in + * the given X509v3 certificate. The return value is allocated and stored into + * ret. + * + * Critical will be either 0 or 1. + * + * If the extension does not exist, GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will + * be returned. + */ +int +_gnutls_x509_crt_get_extension (gnutls_x509_crt_t cert, + const char *extension_id, int indx, + gnutls_datum_t * ret, unsigned int *_critical) +{ + int k, result, len; + char name[MAX_NAME_SIZE], name2[MAX_NAME_SIZE]; + char str[1024]; + char str_critical[10]; + int critical = 0; + char extnID[128]; + gnutls_datum_t value; + int indx_counter = 0; + + ret->data = NULL; + ret->size = 0; + + k = 0; + do + { + k++; + + snprintf (name, sizeof (name), "tbsCertificate.extensions.?%u", k); + + len = sizeof (str) - 1; + result = asn1_read_value (cert->cert, name, str, &len); + + /* move to next + */ + + if (result == ASN1_ELEMENT_NOT_FOUND) + { + break; + } + + do + { + + _gnutls_str_cpy (name2, sizeof (name2), name); + _gnutls_str_cat (name2, sizeof (name2), ".extnID"); + + len = sizeof (extnID) - 1; + result = asn1_read_value (cert->cert, name2, extnID, &len); + + if (result == ASN1_ELEMENT_NOT_FOUND) + { + gnutls_assert (); + break; + } + else if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + /* Handle Extension + */ + if (strcmp (extnID, extension_id) == 0 && indx == indx_counter++) + { + /* extension was found + */ + + /* read the critical status. + */ + _gnutls_str_cpy (name2, sizeof (name2), name); + _gnutls_str_cat (name2, sizeof (name2), ".critical"); + + len = sizeof (str_critical); + result = + asn1_read_value (cert->cert, name2, str_critical, &len); + + if (result == ASN1_ELEMENT_NOT_FOUND) + { + gnutls_assert (); + break; + } + else if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + if (str_critical[0] == 'T') + critical = 1; + else + critical = 0; + + /* read the value. + */ + _gnutls_str_cpy (name2, sizeof (name2), name); + _gnutls_str_cat (name2, sizeof (name2), ".extnValue"); + + result = _gnutls_x509_read_value (cert->cert, name2, &value, 0); + if (result < 0) + { + gnutls_assert (); + return result; + } + + ret->data = value.data; + ret->size = value.size; + + if (_critical) + *_critical = critical; + + return 0; + } + + + } + while (0); + } + while (1); + + if (result == ASN1_ELEMENT_NOT_FOUND) + { + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + else + { + gnutls_assert (); + return _gnutls_asn2err (result); + } +} + +/* This function will attempt to return the requested extension OID found in + * the given X509v3 certificate. + * + * If you have passed the last extension, GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will + * be returned. + */ +int +_gnutls_x509_crt_get_extension_oid (gnutls_x509_crt_t cert, + int indx, void *oid, size_t * sizeof_oid) +{ + int k, result, len; + char name[MAX_NAME_SIZE], name2[MAX_NAME_SIZE]; + char str[1024]; + char extnID[128]; + int indx_counter = 0; + + k = 0; + do + { + k++; + + snprintf (name, sizeof (name), "tbsCertificate.extensions.?%u", k); + + len = sizeof (str) - 1; + result = asn1_read_value (cert->cert, name, str, &len); + + /* move to next + */ + + if (result == ASN1_ELEMENT_NOT_FOUND) + { + break; + } + + do + { + + _gnutls_str_cpy (name2, sizeof (name2), name); + _gnutls_str_cat (name2, sizeof (name2), ".extnID"); + + len = sizeof (extnID) - 1; + result = asn1_read_value (cert->cert, name2, extnID, &len); + + if (result == ASN1_ELEMENT_NOT_FOUND) + { + gnutls_assert (); + break; + } + else if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + /* Handle Extension + */ + if (indx == indx_counter++) + { + len = strlen (extnID) + 1; + + if (*sizeof_oid < (unsigned) len) + { + *sizeof_oid = len; + gnutls_assert (); + return GNUTLS_E_SHORT_MEMORY_BUFFER; + } + + memcpy (oid, extnID, len); + *sizeof_oid = len - 1; + + return 0; + } + + + } + while (0); + } + while (1); + + if (result == ASN1_ELEMENT_NOT_FOUND) + { + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + else + { + gnutls_assert (); + return _gnutls_asn2err (result); + } +} + +/* This function will attempt to set the requested extension in + * the given X509v3 certificate. + * + * Critical will be either 0 or 1. + */ +static int +set_extension (ASN1_TYPE asn, const char *extension_id, + const gnutls_datum_t * ext_data, unsigned int critical) +{ + int result; + const char *str; + + /* Add a new extension in the list. + */ + result = asn1_write_value (asn, "tbsCertificate.extensions", "NEW", 1); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + result = + asn1_write_value (asn, "tbsCertificate.extensions.?LAST.extnID", + extension_id, 1); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + if (critical == 0) + str = "FALSE"; + else + str = "TRUE"; + + + result = + asn1_write_value (asn, "tbsCertificate.extensions.?LAST.critical", + str, 1); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + result = + _gnutls_x509_write_value (asn, + "tbsCertificate.extensions.?LAST.extnValue", + ext_data, 0); + if (result < 0) + { + gnutls_assert (); + return result; + } + + return 0; +} + +/* Overwrite the given extension (using the index) + * index here starts from one. + */ +static int +overwrite_extension (ASN1_TYPE asn, unsigned int indx, + const gnutls_datum_t * ext_data, unsigned int critical) +{ + char name[MAX_NAME_SIZE], name2[MAX_NAME_SIZE]; + const char *str; + int result; + + snprintf (name, sizeof (name), "tbsCertificate.extensions.?%u", indx); + + if (critical == 0) + str = "FALSE"; + else + str = "TRUE"; + + _gnutls_str_cpy (name2, sizeof (name2), name); + _gnutls_str_cat (name2, sizeof (name2), ".critical"); + + result = asn1_write_value (asn, name2, str, 1); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + _gnutls_str_cpy (name2, sizeof (name2), name); + _gnutls_str_cat (name2, sizeof (name2), ".extnValue"); + + result = _gnutls_x509_write_value (asn, name2, ext_data, 0); + if (result < 0) + { + gnutls_assert (); + return result; + } + + return 0; +} + +/* This function will attempt to overwrite the requested extension with + * the given one. + * + * Critical will be either 0 or 1. + */ +int +_gnutls_x509_crt_set_extension (gnutls_x509_crt_t cert, + const char *ext_id, + const gnutls_datum_t * ext_data, + unsigned int critical) +{ + int result; + int k, len; + char name[MAX_NAME_SIZE], name2[MAX_NAME_SIZE]; + char extnID[128]; + + /* Find the index of the given extension. + */ + k = 0; + do + { + k++; + + snprintf (name, sizeof (name), "tbsCertificate.extensions.?%u", k); + + len = sizeof (extnID) - 1; + result = asn1_read_value (cert->cert, name, extnID, &len); + + /* move to next + */ + + if (result == ASN1_ELEMENT_NOT_FOUND) + { + break; + } + + do + { + + _gnutls_str_cpy (name2, sizeof (name2), name); + _gnutls_str_cat (name2, sizeof (name2), ".extnID"); + + len = sizeof (extnID) - 1; + result = asn1_read_value (cert->cert, name2, extnID, &len); + + if (result == ASN1_ELEMENT_NOT_FOUND) + { + gnutls_assert (); + break; + } + else if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + /* Handle Extension + */ + if (strcmp (extnID, ext_id) == 0) + { + /* extension was found + */ + return overwrite_extension (cert->cert, k, ext_data, critical); + } + + + } + while (0); + } + while (1); + + if (result == ASN1_ELEMENT_NOT_FOUND) + { + return set_extension (cert->cert, ext_id, ext_data, critical); + } + else + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + + return 0; +} + + +/* Here we only extract the KeyUsage field, from the DER encoded + * extension. + */ +int +_gnutls_x509_ext_extract_keyUsage (uint16_t * keyUsage, + opaque * extnValue, int extnValueLen) +{ + ASN1_TYPE ext = ASN1_TYPE_EMPTY; + int len, result; + uint8_t str[2]; + + str[0] = str[1] = 0; + *keyUsage = 0; + + if ((result = asn1_create_element + (_gnutls_get_pkix (), "PKIX1.KeyUsage", &ext)) != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + result = asn1_der_decoding (&ext, extnValue, extnValueLen, NULL); + + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + asn1_delete_structure (&ext); + return _gnutls_asn2err (result); + } + + len = sizeof (str); + result = asn1_read_value (ext, "", str, &len); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + asn1_delete_structure (&ext); + return 0; + } + + *keyUsage = str[0] | (str[1] << 8); + + asn1_delete_structure (&ext); + + return 0; +} + +/* extract the basicConstraints from the DER encoded extension + */ +int +_gnutls_x509_ext_extract_basicConstraints (int *CA, + int *pathLenConstraint, + opaque * extnValue, + int extnValueLen) +{ + ASN1_TYPE ext = ASN1_TYPE_EMPTY; + char str[128]; + int len, result; + + if ((result = asn1_create_element + (_gnutls_get_pkix (), "PKIX1.BasicConstraints", &ext)) != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + result = asn1_der_decoding (&ext, extnValue, extnValueLen, NULL); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + asn1_delete_structure (&ext); + return _gnutls_asn2err (result); + } + + if (pathLenConstraint) + { + result = _gnutls_x509_read_uint (ext, "pathLenConstraint", + pathLenConstraint); + if (result == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND) + *pathLenConstraint = -1; + else if (result != GNUTLS_E_SUCCESS) + { + gnutls_assert (); + asn1_delete_structure (&ext); + return _gnutls_asn2err (result); + } + } + + /* the default value of cA is false. + */ + len = sizeof (str) - 1; + result = asn1_read_value (ext, "cA", str, &len); + if (result == ASN1_SUCCESS && strcmp (str, "TRUE") == 0) + *CA = 1; + else + *CA = 0; + + asn1_delete_structure (&ext); + + return 0; +} + +/* generate the basicConstraints in a DER encoded extension + * Use 0 or 1 (TRUE) for CA. + * Use negative values for pathLenConstraint to indicate that the field + * should not be present, >= 0 to indicate set values. + */ +int +_gnutls_x509_ext_gen_basicConstraints (int CA, + int pathLenConstraint, + gnutls_datum_t * der_ext) +{ + ASN1_TYPE ext = ASN1_TYPE_EMPTY; + const char *str; + int result; + + if (CA == 0) + str = "FALSE"; + else + str = "TRUE"; + + result = + asn1_create_element (_gnutls_get_pkix (), "PKIX1.BasicConstraints", &ext); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + result = asn1_write_value (ext, "cA", str, 1); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + asn1_delete_structure (&ext); + return _gnutls_asn2err (result); + } + + if (pathLenConstraint < 0) + { + result = asn1_write_value (ext, "pathLenConstraint", NULL, 0); + if (result < 0) + result = _gnutls_asn2err (result); + } + else + result = _gnutls_x509_write_uint32 (ext, "pathLenConstraint", + pathLenConstraint); + if (result < 0) + { + gnutls_assert (); + asn1_delete_structure (&ext); + return result; + } + + result = _gnutls_x509_der_encode (ext, "", der_ext, 0); + + asn1_delete_structure (&ext); + + if (result < 0) + { + gnutls_assert (); + return result; + } + + return 0; +} + +/* generate the keyUsage in a DER encoded extension + * Use an ORed SEQUENCE of GNUTLS_KEY_* for usage. + */ +int +_gnutls_x509_ext_gen_keyUsage (uint16_t usage, gnutls_datum_t * der_ext) +{ + ASN1_TYPE ext = ASN1_TYPE_EMPTY; + int result; + uint8_t str[2]; + + result = asn1_create_element (_gnutls_get_pkix (), "PKIX1.KeyUsage", &ext); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + str[0] = usage & 0xff; + str[1] = usage >> 8; + + result = asn1_write_value (ext, "", str, 9); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + asn1_delete_structure (&ext); + return _gnutls_asn2err (result); + } + + result = _gnutls_x509_der_encode (ext, "", der_ext, 0); + + asn1_delete_structure (&ext); + + if (result < 0) + { + gnutls_assert (); + return result; + } + + return 0; +} + +static int +write_new_general_name (ASN1_TYPE ext, const char *ext_name, + gnutls_x509_subject_alt_name_t type, + const char *data_string) +{ + const char *str; + int result; + char name[128]; + + result = asn1_write_value (ext, ext_name, "NEW", 1); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + switch (type) + { + case GNUTLS_SAN_DNSNAME: + str = "dNSName"; + break; + case GNUTLS_SAN_RFC822NAME: + str = "rfc822Name"; + break; + case GNUTLS_SAN_URI: + str = "uniformResourceIdentifier"; + break; + case GNUTLS_SAN_IPADDRESS: + str = "iPAddress"; + break; + default: + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + + if (ext_name[0] == 0) + { /* no dot */ + _gnutls_str_cpy (name, sizeof (name), "?LAST"); + } + else + { + _gnutls_str_cpy (name, sizeof (name), ext_name); + _gnutls_str_cat (name, sizeof (name), ".?LAST"); + } + + result = asn1_write_value (ext, name, str, 1); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + _gnutls_str_cat (name, sizeof (name), "."); + _gnutls_str_cat (name, sizeof (name), str); + + result = asn1_write_value (ext, name, data_string, strlen (data_string)); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + asn1_delete_structure (&ext); + return _gnutls_asn2err (result); + } + + return 0; +} + +/* Convert the given name to GeneralNames in a DER encoded extension. + * This is the same as subject alternative name. + */ +int +_gnutls_x509_ext_gen_subject_alt_name (gnutls_x509_subject_alt_name_t + type, const char *data_string, + gnutls_datum_t * der_ext) +{ + ASN1_TYPE ext = ASN1_TYPE_EMPTY; + int result; + + result = + asn1_create_element (_gnutls_get_pkix (), "PKIX1.GeneralNames", &ext); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + result = write_new_general_name (ext, "", type, data_string); + if (result < 0) + { + gnutls_assert (); + asn1_delete_structure (&ext); + return result; + } + + result = _gnutls_x509_der_encode (ext, "", der_ext, 0); + + asn1_delete_structure (&ext); + + if (result < 0) + { + gnutls_assert (); + return result; + } + + return 0; +} + +/* generate the SubjectKeyID in a DER encoded extension + */ +int +_gnutls_x509_ext_gen_key_id (const void *id, size_t id_size, + gnutls_datum_t * der_ext) +{ + ASN1_TYPE ext = ASN1_TYPE_EMPTY; + int result; + + result = + asn1_create_element (_gnutls_get_pkix (), + "PKIX1.SubjectKeyIdentifier", &ext); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + result = asn1_write_value (ext, "", id, id_size); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + asn1_delete_structure (&ext); + return _gnutls_asn2err (result); + } + + result = _gnutls_x509_der_encode (ext, "", der_ext, 0); + + asn1_delete_structure (&ext); + + if (result < 0) + { + gnutls_assert (); + return result; + } + + return 0; +} + +/* generate the AuthorityKeyID in a DER encoded extension + */ +int +_gnutls_x509_ext_gen_auth_key_id (const void *id, size_t id_size, + gnutls_datum_t * der_ext) +{ + ASN1_TYPE ext = ASN1_TYPE_EMPTY; + int result; + + result = + asn1_create_element (_gnutls_get_pkix (), + "PKIX1.AuthorityKeyIdentifier", &ext); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + result = asn1_write_value (ext, "keyIdentifier", id, id_size); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + asn1_delete_structure (&ext); + return _gnutls_asn2err (result); + } + + asn1_write_value (ext, "authorityCertIssuer", NULL, 0); + asn1_write_value (ext, "authorityCertSerialNumber", NULL, 0); + + result = _gnutls_x509_der_encode (ext, "", der_ext, 0); + + asn1_delete_structure (&ext); + + if (result < 0) + { + gnutls_assert (); + return result; + } + + return 0; +} + + +/* Creates and encodes the CRL Distribution points. data_string should be a name + * and type holds the type of the name. + * reason_flags should be an or'ed sequence of GNUTLS_CRL_REASON_*. + * + */ +int +_gnutls_x509_ext_gen_crl_dist_points (gnutls_x509_subject_alt_name_t + type, const void *data_string, + unsigned int reason_flags, + gnutls_datum_t * der_ext) +{ + ASN1_TYPE ext = ASN1_TYPE_EMPTY; + gnutls_datum_t gnames = { NULL, 0 }; + int result; + uint8_t reasons[2]; + + reasons[0] = reason_flags & 0xff; + reasons[1] = reason_flags >> 8; + + result = + asn1_create_element (_gnutls_get_pkix (), + "PKIX1.CRLDistributionPoints", &ext); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + result = asn1_write_value (ext, "", "NEW", 1); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + if (reason_flags) + { + result = asn1_write_value (ext, "?LAST.reasons", reasons, 9); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + } + else + { + result = asn1_write_value (ext, "?LAST.reasons", NULL, 0); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + } + + result = asn1_write_value (ext, "?LAST.cRLIssuer", NULL, 0); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + /* When used as type CHOICE. + */ + result = asn1_write_value (ext, "?LAST.distributionPoint", "fullName", 1); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + +#if 0 + /* only needed in old code (where defined as SEQUENCE OF) */ + asn1_write_value (ext, + "?LAST.distributionPoint.nameRelativeToCRLIssuer", + NULL, 0); +#endif + + result = + write_new_general_name (ext, "?LAST.distributionPoint.fullName", + type, data_string); + if (result < 0) + { + gnutls_assert (); + goto cleanup; + } + + result = _gnutls_x509_der_encode (ext, "", der_ext, 0); + + if (result < 0) + { + gnutls_assert (); + goto cleanup; + } + + result = 0; + +cleanup: + _gnutls_free_datum (&gnames); + asn1_delete_structure (&ext); + + return result; +} + +/* extract the proxyCertInfo from the DER encoded extension + */ +int +_gnutls_x509_ext_extract_proxyCertInfo (int *pathLenConstraint, + char **policyLanguage, + char **policy, + size_t * sizeof_policy, + opaque * extnValue, int extnValueLen) +{ + ASN1_TYPE ext = ASN1_TYPE_EMPTY; + int result; + gnutls_datum_t value; + + if ((result = asn1_create_element + (_gnutls_get_pkix (), "PKIX1.ProxyCertInfo", &ext)) != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + result = asn1_der_decoding (&ext, extnValue, extnValueLen, NULL); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + asn1_delete_structure (&ext); + return _gnutls_asn2err (result); + } + + if (pathLenConstraint) + { + result = _gnutls_x509_read_uint (ext, "pCPathLenConstraint", + pathLenConstraint); + if (result == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND) + *pathLenConstraint = -1; + else if (result != GNUTLS_E_SUCCESS) + { + asn1_delete_structure (&ext); + return _gnutls_asn2err (result); + } + } + + result = _gnutls_x509_read_value (ext, "proxyPolicy.policyLanguage", + &value, 0); + if (result < 0) + { + gnutls_assert (); + asn1_delete_structure (&ext); + return result; + } + + if (policyLanguage) + *policyLanguage = gnutls_strdup (value.data); + + result = _gnutls_x509_read_value (ext, "proxyPolicy.policy", &value, 0); + if (result == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND) + { + if (policy) + *policy = NULL; + if (sizeof_policy) + *sizeof_policy = 0; + } + else if (result < 0) + { + gnutls_assert (); + asn1_delete_structure (&ext); + return result; + } + else + { + if (policy) + *policy = value.data; + if (sizeof_policy) + *sizeof_policy = value.size; + } + + asn1_delete_structure (&ext); + + return 0; +} + +/* generate the proxyCertInfo in a DER encoded extension + */ +int +_gnutls_x509_ext_gen_proxyCertInfo (int pathLenConstraint, + const char *policyLanguage, + const char *policy, + size_t sizeof_policy, + gnutls_datum_t * der_ext) +{ + ASN1_TYPE ext = ASN1_TYPE_EMPTY; + int result; + + result = asn1_create_element (_gnutls_get_pkix (), + "PKIX1.ProxyCertInfo", &ext); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + if (pathLenConstraint < 0) + { + result = asn1_write_value (ext, "pCPathLenConstraint", NULL, 0); + if (result < 0) + result = _gnutls_asn2err (result); + } + else + result = _gnutls_x509_write_uint32 (ext, "pCPathLenConstraint", + pathLenConstraint); + if (result < 0) + { + gnutls_assert (); + asn1_delete_structure (&ext); + return result; + } + + result = asn1_write_value (ext, "proxyPolicy.policyLanguage", + policyLanguage, 1); + if (result < 0) + { + gnutls_assert (); + asn1_delete_structure (&ext); + return _gnutls_asn2err (result); + } + + result = asn1_write_value (ext, "proxyPolicy.policy", + policy, sizeof_policy); + if (result < 0) + { + gnutls_assert (); + asn1_delete_structure (&ext); + return _gnutls_asn2err (result); + } + + result = _gnutls_x509_der_encode (ext, "", der_ext, 0); + + asn1_delete_structure (&ext); + + if (result < 0) + { + gnutls_assert (); + return result; + } + + return 0; +} diff --git a/src/daemon/https/x509/extensions.h b/src/daemon/https/x509/extensions.h new file mode 100644 index 00000000..fb758c90 --- /dev/null +++ b/src/daemon/https/x509/extensions.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2006, 2007 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +int _gnutls_x509_crt_get_extension (gnutls_x509_crt_t cert, + const char *extension_id, int indx, + gnutls_datum_t * ret, + unsigned int *critical); + +int _gnutls_x509_crt_get_extension_oid (gnutls_x509_crt_t cert, + int indx, void *ret, + size_t * ret_size); +int _gnutls_x509_ext_extract_keyUsage (uint16_t * keyUsage, + opaque * extnValue, int extnValueLen); +int _gnutls_x509_ext_extract_basicConstraints (int *CA, + int *pathLenConstraint, + opaque * extnValue, + int extnValueLen); +int _gnutls_x509_crt_set_extension (gnutls_x509_crt_t cert, + const char *extension_id, + const gnutls_datum_t * ext_data, + unsigned int critical); +int _gnutls_x509_ext_gen_basicConstraints (int CA, int pathLenConstraint, + gnutls_datum_t * der_ext); +int _gnutls_x509_ext_gen_keyUsage (uint16_t usage, gnutls_datum_t * der_ext); +int _gnutls_x509_ext_gen_subject_alt_name (gnutls_x509_subject_alt_name_t + type, const char *data_string, + gnutls_datum_t * der_ext); +int _gnutls_x509_ext_gen_crl_dist_points (gnutls_x509_subject_alt_name_t + type, const void *data_string, + unsigned int reason_flags, + gnutls_datum_t * der_ext); +int _gnutls_x509_ext_gen_key_id (const void *id, size_t id_size, + gnutls_datum_t * der_data); +int _gnutls_x509_ext_gen_auth_key_id (const void *id, size_t id_size, + gnutls_datum_t * der_data); + +int _gnutls_x509_ext_extract_proxyCertInfo (int *pathLenConstraint, + char **policyLanguage, + char **policy, + size_t *sizeof_policy, + opaque * extnValue, + int extnValueLen); +int _gnutls_x509_ext_gen_proxyCertInfo (int pathLenConstraint, + const char *policyLanguage, + const char *policy, + size_t sizeof_policy, + gnutls_datum_t * der_ext); diff --git a/src/daemon/https/x509/mpi.c b/src/daemon/https/x509/mpi.c new file mode 100644 index 00000000..8cc43855 --- /dev/null +++ b/src/daemon/https/x509/mpi.c @@ -0,0 +1,576 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2007 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +#include +#include +#include +#include +#include +#include "common.h" +#include "x509.h" +#include +#include "mpi.h" + +/* + * some x509 certificate parsing functions that relate to MPI parameter + * extraction. This reads the BIT STRING subjectPublicKey. + * Returns 2 parameters (m,e). + */ +int +_gnutls_x509_read_rsa_params (opaque * der, int dersize, mpi_t * params) +{ + int result; + ASN1_TYPE spk = ASN1_TYPE_EMPTY; + + if ((result = + asn1_create_element (_gnutls_get_gnutls_asn (), "GNUTLS.RSAPublicKey", + &spk)) != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + result = asn1_der_decoding (&spk, der, dersize, NULL); + + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + asn1_delete_structure (&spk); + return _gnutls_asn2err (result); + } + + if ((result = _gnutls_x509_read_int (spk, "modulus", ¶ms[0])) < 0) + { + gnutls_assert (); + asn1_delete_structure (&spk); + return GNUTLS_E_ASN1_GENERIC_ERROR; + } + + if ((result = + _gnutls_x509_read_int (spk, "publicExponent", ¶ms[1])) < 0) + { + gnutls_assert (); + _gnutls_mpi_release (¶ms[0]); + asn1_delete_structure (&spk); + return GNUTLS_E_ASN1_GENERIC_ERROR; + } + + asn1_delete_structure (&spk); + + return 0; + +} + +/* reads p,q and g + * from the certificate (subjectPublicKey BIT STRING). + * params[0-2] + */ +int +_gnutls_x509_read_dsa_params (opaque * der, int dersize, mpi_t * params) +{ + int result; + ASN1_TYPE spk = ASN1_TYPE_EMPTY; + + if ((result = + asn1_create_element (_gnutls_get_pkix (), "PKIX1.Dss-Parms", + &spk)) != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + result = asn1_der_decoding (&spk, der, dersize, NULL); + + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + asn1_delete_structure (&spk); + return _gnutls_asn2err (result); + } + + /* FIXME: If the parameters are not included in the certificate + * then the issuer's parameters should be used. This is not + * done yet. + */ + + /* Read p */ + + if ((result = _gnutls_x509_read_int (spk, "p", ¶ms[0])) < 0) + { + gnutls_assert (); + asn1_delete_structure (&spk); + return GNUTLS_E_ASN1_GENERIC_ERROR; + } + + /* Read q */ + + if ((result = _gnutls_x509_read_int (spk, "q", ¶ms[1])) < 0) + { + gnutls_assert (); + asn1_delete_structure (&spk); + _gnutls_mpi_release (¶ms[0]); + return GNUTLS_E_ASN1_GENERIC_ERROR; + } + + /* Read g */ + + if ((result = _gnutls_x509_read_int (spk, "g", ¶ms[2])) < 0) + { + gnutls_assert (); + asn1_delete_structure (&spk); + _gnutls_mpi_release (¶ms[0]); + _gnutls_mpi_release (¶ms[1]); + return GNUTLS_E_ASN1_GENERIC_ERROR; + } + + asn1_delete_structure (&spk); + + return 0; + +} + +/* Reads an Integer from the DER encoded data + */ + +int +_gnutls_x509_read_der_int (opaque * der, int dersize, mpi_t * out) +{ + int result; + ASN1_TYPE spk = ASN1_TYPE_EMPTY; + + /* == INTEGER */ + if ((result = + asn1_create_element (_gnutls_get_gnutls_asn (), "GNUTLS.DSAPublicKey", + &spk)) != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + result = asn1_der_decoding (&spk, der, dersize, NULL); + + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + asn1_delete_structure (&spk); + return _gnutls_asn2err (result); + } + + /* Read Y */ + + if ((result = _gnutls_x509_read_int (spk, "", out)) < 0) + { + gnutls_assert (); + asn1_delete_structure (&spk); + return _gnutls_asn2err (result); + } + + asn1_delete_structure (&spk); + + return 0; + +} + +/* reads DSA's Y + * from the certificate + * only sets params[3] + */ +int +_gnutls_x509_read_dsa_pubkey (opaque * der, int dersize, mpi_t * params) +{ + return _gnutls_x509_read_der_int (der, dersize, ¶ms[3]); +} + +/* Extracts DSA and RSA parameters from a certificate. + */ +int +_gnutls_x509_crt_get_mpis (gnutls_x509_crt_t cert, + mpi_t * params, int *params_size) +{ + int result; + int pk_algorithm; + gnutls_datum tmp = { NULL, 0 }; + + /* Read the algorithm's OID + */ + pk_algorithm = gnutls_x509_crt_get_pk_algorithm (cert, NULL); + + /* Read the algorithm's parameters + */ + result + = _gnutls_x509_read_value (cert->cert, + "tbsCertificate.subjectPublicKeyInfo.subjectPublicKey", + &tmp, 2); + + if (result < 0) + { + gnutls_assert (); + return result; + } + + switch (pk_algorithm) + { + case GNUTLS_PK_RSA: + /* params[0] is the modulus, + * params[1] is the exponent + */ + if (*params_size < RSA_PUBLIC_PARAMS) + { + gnutls_assert (); + /* internal error. Increase the mpi_ts in params */ + result = GNUTLS_E_INTERNAL_ERROR; + goto error; + } + + if ((result = + _gnutls_x509_read_rsa_params (tmp.data, tmp.size, params)) < 0) + { + gnutls_assert (); + goto error; + } + *params_size = RSA_PUBLIC_PARAMS; + + break; + default: + /* other types like DH + * currently not supported + */ + gnutls_assert (); + result = GNUTLS_E_X509_CERTIFICATE_ERROR; + goto error; + } + + result = 0; + +error:_gnutls_free_datum (&tmp); + return result; +} + +/* + * some x509 certificate functions that relate to MPI parameter + * setting. This writes the BIT STRING subjectPublicKey. + * Needs 2 parameters (m,e). + * + * Allocates the space used to store the DER data. + */ +int +_gnutls_x509_write_rsa_params (mpi_t * params, + int params_size, gnutls_datum_t * der) +{ + int result; + ASN1_TYPE spk = ASN1_TYPE_EMPTY; + + der->data = NULL; + der->size = 0; + + if (params_size < 2) + { + gnutls_assert (); + result = GNUTLS_E_INVALID_REQUEST; + goto cleanup; + } + + if ((result = + asn1_create_element (_gnutls_get_gnutls_asn (), "GNUTLS.RSAPublicKey", + &spk)) != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + result = _gnutls_x509_write_int (spk, "modulus", params[0], 0); + if (result < 0) + { + gnutls_assert (); + goto cleanup; + } + + result = _gnutls_x509_write_int (spk, "publicExponent", params[1], 0); + if (result < 0) + { + gnutls_assert (); + goto cleanup; + } + + result = _gnutls_x509_der_encode (spk, "", der, 0); + if (result < 0) + { + gnutls_assert (); + goto cleanup; + } + + asn1_delete_structure (&spk); + return 0; + +cleanup:asn1_delete_structure (&spk); + + return result; +} + +/* + * This function writes and encodes the parameters for DSS or RSA keys. + * This is the "signatureAlgorithm" fields. + */ +int +_gnutls_x509_write_sig_params (ASN1_TYPE dst, + const char *dst_name, + gnutls_pk_algorithm_t pk_algorithm, + gnutls_digest_algorithm_t dig, + mpi_t * params, int params_size) +{ + gnutls_datum_t der; + int result; + char name[128]; + const char *pk; + + _gnutls_str_cpy (name, sizeof (name), dst_name); + _gnutls_str_cat (name, sizeof (name), ".algorithm"); + + pk = _gnutls_x509_sign_to_oid (pk_algorithm, HASH2MAC (dig)); + if (pk == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + /* write the OID. + */ + result = asn1_write_value (dst, name, pk, 1); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + _gnutls_str_cpy (name, sizeof (name), dst_name); + _gnutls_str_cat (name, sizeof (name), ".parameters"); + + if (pk_algorithm == GNUTLS_PK_RSA) + { /* RSA */ + result = asn1_write_value (dst, name, NULL, 0); + + if (result != ASN1_SUCCESS && result != ASN1_ELEMENT_NOT_FOUND) + { + /* Here we ignore the element not found error, since this + * may have been disabled before. + */ + gnutls_assert (); + return _gnutls_asn2err (result); + } + } + + return 0; +} + +/* + * This function writes the parameters for DSS keys. + * Needs 3 parameters (p,q,g). + * + * Allocates the space used to store the DER data. + */ +int +_gnutls_x509_write_dsa_params (mpi_t * params, + int params_size, gnutls_datum_t * der) +{ + int result; + ASN1_TYPE spk = ASN1_TYPE_EMPTY; + + der->data = NULL; + der->size = 0; + + if (params_size < 3) + { + gnutls_assert (); + result = GNUTLS_E_INVALID_REQUEST; + goto cleanup; + } + + if ((result = + asn1_create_element (_gnutls_get_gnutls_asn (), "GNUTLS.DSAParameters", + &spk)) != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + result = _gnutls_x509_write_int (spk, "p", params[0], 0); + if (result < 0) + { + gnutls_assert (); + goto cleanup; + } + + result = _gnutls_x509_write_int (spk, "q", params[1], 0); + if (result < 0) + { + gnutls_assert (); + goto cleanup; + } + + result = _gnutls_x509_write_int (spk, "g", params[2], 0); + if (result < 0) + { + gnutls_assert (); + goto cleanup; + } + + result = _gnutls_x509_der_encode (spk, "", der, 0); + if (result < 0) + { + gnutls_assert (); + goto cleanup; + } + + result = 0; + +cleanup:asn1_delete_structure (&spk); + return result; +} + +/* + * This function writes the public parameters for DSS keys. + * Needs 1 parameter (y). + * + * Allocates the space used to store the DER data. + */ +int +_gnutls_x509_write_dsa_public_key (mpi_t * params, + int params_size, gnutls_datum_t * der) +{ + int result; + ASN1_TYPE spk = ASN1_TYPE_EMPTY; + + der->data = NULL; + der->size = 0; + + if (params_size < 3) + { + gnutls_assert (); + result = GNUTLS_E_INVALID_REQUEST; + goto cleanup; + } + + if ((result = + asn1_create_element (_gnutls_get_gnutls_asn (), "GNUTLS.DSAPublicKey", + &spk)) != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + result = _gnutls_x509_write_int (spk, "", params[3], 0); + if (result < 0) + { + gnutls_assert (); + goto cleanup; + } + + result = _gnutls_x509_der_encode (spk, "", der, 0); + if (result < 0) + { + gnutls_assert (); + goto cleanup; + } + + asn1_delete_structure (&spk); + return 0; + +cleanup:asn1_delete_structure (&spk); + return result; +} + +/* this function reads a (small) unsigned integer + * from asn1 structs. Combines the read and the convertion + * steps. + */ +int +_gnutls_x509_read_uint (ASN1_TYPE node, const char *value, unsigned int *ret) +{ + int len, result; + opaque *tmpstr; + + len = 0; + result = asn1_read_value (node, value, NULL, &len); + if (result != ASN1_MEM_ERROR) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + tmpstr = gnutls_alloca (len); + if (tmpstr == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + result = asn1_read_value (node, value, tmpstr, &len); + + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + gnutls_afree (tmpstr); + return _gnutls_asn2err (result); + } + + if (len == 1) + *ret = tmpstr[0]; + else if (len == 2) + *ret = _gnutls_read_uint16 (tmpstr); + else if (len == 3) + *ret = _gnutls_read_uint24 (tmpstr); + else if (len == 4) + *ret = _gnutls_read_uint32 (tmpstr); + else + { + gnutls_assert (); + gnutls_afree (tmpstr); + return GNUTLS_E_INTERNAL_ERROR; + } + + gnutls_afree (tmpstr); + + return 0; +} + +/* Writes the specified integer into the specified node. + */ +int +_gnutls_x509_write_uint32 (ASN1_TYPE node, const char *value, uint32_t num) +{ + opaque tmpstr[4]; + int result; + + _gnutls_write_uint32 (num, tmpstr); + + result = asn1_write_value (node, value, tmpstr, 4); + + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + return 0; +} diff --git a/src/daemon/https/x509/mpi.h b/src/daemon/https/x509/mpi.h new file mode 100644 index 00000000..785d7e02 --- /dev/null +++ b/src/daemon/https/x509/mpi.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +#include +#include "x509.h" + +int _gnutls_x509_crt_get_mpis (gnutls_x509_crt_t cert, + mpi_t * params, int *params_size); +int _gnutls_x509_read_rsa_params (opaque * der, int dersize, mpi_t * params); +int _gnutls_x509_read_dsa_pubkey (opaque * der, int dersize, mpi_t * params); +int _gnutls_x509_read_dsa_params (opaque * der, int dersize, mpi_t * params); + +int _gnutls_x509_write_rsa_params (mpi_t * params, int params_size, + gnutls_datum_t * der); +int _gnutls_x509_write_dsa_params (mpi_t * params, int params_size, + gnutls_datum_t * der); +int _gnutls_x509_write_dsa_public_key (mpi_t * params, int params_size, + gnutls_datum_t * der); + +int _gnutls_x509_read_uint (ASN1_TYPE node, const char *value, + unsigned int *ret); + +int +_gnutls_x509_read_der_int (opaque * der, int dersize, mpi_t* out); + +int _gnutls_x509_read_int (ASN1_TYPE node, const char *value, + mpi_t * ret_mpi); +int _gnutls_x509_write_int (ASN1_TYPE node, const char *value, mpi_t mpi, + int lz); +int _gnutls_x509_write_uint32 (ASN1_TYPE node, const char *value, + uint32_t num); + +int _gnutls_x509_write_sig_params (ASN1_TYPE dst, const char *dst_name, + gnutls_pk_algorithm_t pk_algorithm, + gnutls_digest_algorithm_t, mpi_t * params, + int params_size); diff --git a/src/daemon/https/x509/output.c b/src/daemon/https/x509/output.c new file mode 100644 index 00000000..dcc87ab1 --- /dev/null +++ b/src/daemon/https/x509/output.c @@ -0,0 +1,1360 @@ +/* + * Copyright (C) 2007 Free Software Foundation + * + * Author: Simon Josefsson + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + +/* Functions for printing X.509 Certificate structures + */ + +#include +#include +#include +#include +#include + +/* I18n of error codes. */ +#include "gettext.h" +#define _(String) dgettext (PACKAGE, String) +#define N_(String) gettext_noop (String) + +#define addf _gnutls_string_append_printf +#define adds _gnutls_string_append_str + +static void +hexdump (gnutls_string * str, const char *data, size_t len, const char *spc) +{ + size_t j; + + if (spc) + adds (str, spc); + for (j = 0; j < len; j++) + { + if (((j + 1) % 16) == 0) + { + addf (str, "%.2x\n", (unsigned char) data[j]); + if (spc && j != (len - 1)) + adds (str, spc); + } + else if (j == (len - 1)) + addf (str, "%.2x", (unsigned char) data[j]); + else + addf (str, "%.2x:", (unsigned char) data[j]); + } + if ((j % 16) != 0) + adds (str, "\n"); +} + +static void +hexprint (gnutls_string * str, const char *data, size_t len) +{ + size_t j; + + if (len == 0) + adds (str, "00"); + else + { + for (j = 0; j < len; j++) + addf (str, "%.2x", (unsigned char) data[j]); + } +} + + +static void +asciiprint (gnutls_string * str, const char *data, size_t len) +{ + size_t j; + + for (j = 0; j < len; j++) + if (isprint (data[j])) + addf (str, "%c", (unsigned char) data[j]); + else + addf (str, "."); +} + +static void +print_proxy (gnutls_string * str, gnutls_x509_crt_t cert) +{ + int pathlen; + char *policyLanguage; + char *policy; + size_t npolicy; + int err; + + err = gnutls_x509_crt_get_proxy (cert, NULL, + &pathlen, &policyLanguage, + &policy, &npolicy); + if (err < 0) + { + addf (str, "error: get_proxy: %s\n", gnutls_strerror (err)); + return; + } + + if (pathlen >= 0) + addf (str, _("\t\t\tPath Length Constraint: %d\n"), pathlen); + addf (str, _("\t\t\tPolicy Language: %s"), policyLanguage); + if (strcmp (policyLanguage, "1.3.6.1.5.5.7.21.1") == 0) + adds (str, " (id-ppl-inheritALL)\n"); + else if (strcmp (policyLanguage, "1.3.6.1.5.5.7.21.2") == 0) + adds (str, " (id-ppl-independent)\n"); + else + adds (str, "\n"); + if (npolicy) + { + adds (str, _("\t\t\tPolicy:\n\t\t\t\tASCII: ")); + asciiprint (str, policy, npolicy); + adds (str, _("\n\t\t\t\tHexdump: ")); + hexprint (str, policy, npolicy); + adds (str, "\n"); + } +} + +static void +print_ski (gnutls_string * str, gnutls_x509_crt_t cert) +{ + char *buffer = NULL; + size_t size = 0; + int err; + + err = gnutls_x509_crt_get_subject_key_id (cert, buffer, &size, NULL); + if (err != GNUTLS_E_SHORT_MEMORY_BUFFER) + { + addf (str, "error: get_subject_key_id: %s\n", gnutls_strerror (err)); + return; + } + + buffer = gnutls_malloc (size); + if (!buffer) + { + addf (str, "error: malloc: %s\n", gnutls_strerror (err)); + return; + } + + err = gnutls_x509_crt_get_subject_key_id (cert, buffer, &size, NULL); + if (err < 0) + { + gnutls_free (buffer); + addf (str, "error: get_subject_key_id2: %s\n", gnutls_strerror (err)); + return; + } + + adds (str, "\t\t\t"); + hexprint (str, buffer, size); + adds (str, "\n"); + + gnutls_free (buffer); +} + +static void +print_aki (gnutls_string * str, gnutls_x509_crt_t cert) +{ + char *buffer = NULL; + size_t size = 0; + int err; + + err = gnutls_x509_crt_get_authority_key_id (cert, buffer, &size, NULL); + if (err != GNUTLS_E_SHORT_MEMORY_BUFFER) + { + addf (str, "error: get_authority_key_id: %s\n", gnutls_strerror (err)); + return; + } + + buffer = gnutls_malloc (size); + if (!buffer) + { + addf (str, "error: malloc: %s\n", gnutls_strerror (err)); + return; + } + + err = gnutls_x509_crt_get_authority_key_id (cert, buffer, &size, NULL); + if (err < 0) + { + gnutls_free (buffer); + addf (str, "error: get_authority_key_id2: %s\n", gnutls_strerror (err)); + return; + } + + adds (str, "\t\t\t"); + hexprint (str, buffer, size); + adds (str, "\n"); + + gnutls_free (buffer); +} + +static void +print_key_usage (gnutls_string * str, gnutls_x509_crt_t cert) +{ + unsigned int key_usage; + int err; + + err = gnutls_x509_crt_get_key_usage (cert, &key_usage, NULL); + if (err < 0) + { + addf (str, "error: get_key_usage: %s\n", gnutls_strerror (err)); + return; + } + + if (key_usage & GNUTLS_KEY_DIGITAL_SIGNATURE) + addf (str, _("\t\t\tDigital signature.\n")); + if (key_usage & GNUTLS_KEY_NON_REPUDIATION) + addf (str, _("\t\t\tNon repudiation.\n")); + if (key_usage & GNUTLS_KEY_KEY_ENCIPHERMENT) + addf (str, _("\t\t\tKey encipherment.\n")); + if (key_usage & GNUTLS_KEY_DATA_ENCIPHERMENT) + addf (str, _("\t\t\tData encipherment.\n")); + if (key_usage & GNUTLS_KEY_KEY_AGREEMENT) + addf (str, _("\t\t\tKey agreement.\n")); + if (key_usage & GNUTLS_KEY_KEY_CERT_SIGN) + addf (str, _("\t\t\tCertificate signing.\n")); + if (key_usage & GNUTLS_KEY_CRL_SIGN) + addf (str, _("\t\t\tCRL signing.\n")); + if (key_usage & GNUTLS_KEY_ENCIPHER_ONLY) + addf (str, _("\t\t\tKey encipher only.\n")); + if (key_usage & GNUTLS_KEY_DECIPHER_ONLY) + addf (str, _("\t\t\tKey decipher only.\n")); +} + +static void +print_crldist (gnutls_string * str, gnutls_x509_crt_t cert) +{ + char *buffer = NULL; + size_t size; + int err; + int indx; + + for (indx = 0;; indx++) + { + size = 0; + err = gnutls_x509_crt_get_crl_dist_points (cert, indx, buffer, &size, + NULL, NULL); + if (err == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) + return; + if (err != GNUTLS_E_SHORT_MEMORY_BUFFER) + { + addf (str, "error: get_crl_dist_points: %s\n", + gnutls_strerror (err)); + return; + } + + buffer = gnutls_malloc (size); + if (!buffer) + { + addf (str, "error: malloc: %s\n", gnutls_strerror (err)); + return; + } + + err = gnutls_x509_crt_get_crl_dist_points (cert, indx, buffer, &size, + NULL, NULL); + if (err < 0) + { + gnutls_free (buffer); + addf (str, "error: get_crl_dist_points2: %s\n", + gnutls_strerror (err)); + return; + } + + switch (err) + { + case GNUTLS_SAN_DNSNAME: + addf (str, "\t\t\tDNSname: %.*s\n", size, buffer); + break; + + case GNUTLS_SAN_RFC822NAME: + addf (str, "\t\t\tRFC822name: %.*s\n", size, buffer); + break; + + case GNUTLS_SAN_URI: + addf (str, "\t\t\tURI: %.*s\n", size, buffer); + break; + + case GNUTLS_SAN_IPADDRESS: + addf (str, "\t\t\tIPAddress: %.*s\n", size, buffer); + break; + + case GNUTLS_SAN_DN: + addf (str, "\t\t\tdirectoryName: %.*s\n", size, buffer); + break; + + default: + addf (str, "error: unknown SAN\n"); + break; + } + gnutls_free (buffer); + } +} + +static void +print_key_purpose (gnutls_string * str, gnutls_x509_crt_t cert) +{ + int indx; + char *buffer = NULL; + size_t size; + int err; + + for (indx = 0;; indx++) + { + size = 0; + err = gnutls_x509_crt_get_key_purpose_oid (cert, indx, buffer, + &size, NULL); + if (err == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) + return; + if (err != GNUTLS_E_SHORT_MEMORY_BUFFER) + { + addf (str, "error: get_key_purpose_oid: %s\n", + gnutls_strerror (err)); + return; + } + + buffer = gnutls_malloc (size); + if (!buffer) + { + addf (str, "error: malloc: %s\n", gnutls_strerror (err)); + return; + } + + err = gnutls_x509_crt_get_key_purpose_oid (cert, indx, buffer, + &size, NULL); + if (err < 0) + { + gnutls_free (buffer); + addf (str, "error: get_key_purpose_oid2: %s\n", + gnutls_strerror (err)); + return; + } + + if (strcmp (buffer, GNUTLS_KP_TLS_WWW_SERVER) == 0) + addf (str, _("\t\t\tTLS WWW Server.\n")); + else if (strcmp (buffer, GNUTLS_KP_TLS_WWW_CLIENT) == 0) + addf (str, _("\t\t\tTLS WWW Client.\n")); + else if (strcmp (buffer, GNUTLS_KP_CODE_SIGNING) == 0) + addf (str, _("\t\t\tCode signing.\n")); + else if (strcmp (buffer, GNUTLS_KP_EMAIL_PROTECTION) == 0) + addf (str, _("\t\t\tEmail protection.\n")); + else if (strcmp (buffer, GNUTLS_KP_TIME_STAMPING) == 0) + addf (str, _("\t\t\tTime stamping.\n")); + else if (strcmp (buffer, GNUTLS_KP_OCSP_SIGNING) == 0) + addf (str, _("\t\t\tOCSP signing.\n")); + else if (strcmp (buffer, GNUTLS_KP_ANY) == 0) + addf (str, _("\t\t\tAny purpose.\n")); + else + addf (str, "\t\t\t%s\n", buffer); + + gnutls_free (buffer); + } +} + +static void +print_basic (gnutls_string * str, gnutls_x509_crt_t cert) +{ + int pathlen; + int err; + + err = gnutls_x509_crt_get_basic_constraints (cert, NULL, NULL, &pathlen); + if (err < 0) + { + addf (str, "error: get_basic_constraints: %s\n", gnutls_strerror (err)); + return; + } + + if (err == 0) + addf (str, _("\t\t\tCertificate Authority (CA): FALSE\n")); + else + addf (str, _("\t\t\tCertificate Authority (CA): TRUE\n")); + + if (pathlen >= 0) + addf (str, _("\t\t\tPath Length Constraint: %d\n"), pathlen); +} + +static void +print_san (gnutls_string * str, gnutls_x509_crt_t cert) +{ + unsigned int san_idx; + + for (san_idx = 0;; san_idx++) + { + char *buffer = NULL; + size_t size = 0; + int err; + + err = + gnutls_x509_crt_get_subject_alt_name (cert, san_idx, buffer, &size, + NULL); + if (err == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) + break; + if (err != GNUTLS_E_SHORT_MEMORY_BUFFER) + { + addf (str, "error: get_subject_alt_name: %s\n", + gnutls_strerror (err)); + return; + } + + buffer = gnutls_malloc (size); + if (!buffer) + { + addf (str, "error: malloc: %s\n", gnutls_strerror (err)); + return; + } + + err = gnutls_x509_crt_get_subject_alt_name (cert, san_idx, + buffer, &size, NULL); + if (err < 0) + { + gnutls_free (buffer); + addf (str, "error: get_subject_alt_name2: %s\n", + gnutls_strerror (err)); + return; + } + + switch (err) + { + case GNUTLS_SAN_DNSNAME: + addf (str, "\t\t\tDNSname: %.*s\n", size, buffer); + break; + + case GNUTLS_SAN_RFC822NAME: + addf (str, "\t\t\tRFC822name: %.*s\n", size, buffer); + break; + + case GNUTLS_SAN_URI: + addf (str, "\t\t\tURI: %.*s\n", size, buffer); + break; + + case GNUTLS_SAN_IPADDRESS: + addf (str, "\t\t\tIPAddress: %.*s\n", size, buffer); + break; + + case GNUTLS_SAN_DN: + addf (str, "\t\t\tdirectoryName: %.*s\n", size, buffer); + break; + + case GNUTLS_SAN_OTHERNAME: + { + char *oid = NULL; + size_t oidsize; + + oidsize = 0; + err = gnutls_x509_crt_get_subject_alt_othername_oid + (cert, san_idx, oid, &oidsize); + if (err != GNUTLS_E_SHORT_MEMORY_BUFFER) + { + gnutls_free (buffer); + addf (str, "error: get_subject_alt_othername_oid: %s\n", + gnutls_strerror (err)); + return; + } + + oid = gnutls_malloc (oidsize); + if (!oid) + { + gnutls_free (buffer); + addf (str, "error: malloc: %s\n", gnutls_strerror (err)); + return; + } + + err = gnutls_x509_crt_get_subject_alt_othername_oid + (cert, san_idx, oid, &oidsize); + if (err < 0) + { + gnutls_free (buffer); + gnutls_free (oid); + addf (str, "error: get_subject_alt_othername_oid2: %s\n", + gnutls_strerror (err)); + return; + } + + if (err == GNUTLS_SAN_OTHERNAME_XMPP) + addf (str, _("\t\t\tXMPP Address: %.*s\n"), size, buffer); + else + { + addf (str, _("\t\t\totherName OID: %.*s\n"), oidsize, oid); + addf (str, _("\t\t\totherName DER: ")); + hexprint (str, buffer, size); + addf (str, _("\n\t\t\totherName ASCII: ")); + asciiprint (str, buffer, size); + addf (str, "\n"); + } + gnutls_free (oid); + } + break; + + default: + addf (str, "error: unknown SAN\n"); + break; + } + + gnutls_free (buffer); + } +} + +static void +print_cert (gnutls_string * str, gnutls_x509_crt_t cert, int notsigned) +{ + /* Version. */ + { + int version = gnutls_x509_crt_get_version (cert); + if (version < 0) + addf (str, "error: get_version: %s\n", gnutls_strerror (version)); + else + addf (str, _("\tVersion: %d\n"), version); + } + + /* Serial. */ + { + char serial[128]; + size_t serial_size = sizeof (serial); + int err; + + err = gnutls_x509_crt_get_serial (cert, serial, &serial_size); + if (err < 0) + addf (str, "error: get_serial: %s\n", gnutls_strerror (err)); + else + { + addf (str, _("\tSerial Number (hex): ")); + hexprint (str, serial, serial_size); + addf (str, "\n"); + } + } + + /* Issuer. */ + if (!notsigned) + { + char dn[1024]; + size_t dn_size = sizeof (dn); + int err; + + err = gnutls_x509_crt_get_issuer_dn (cert, dn, &dn_size); + if (err < 0) + addf (str, "error: get_issuer_dn: %s\n", gnutls_strerror (err)); + else + addf (str, _("\tIssuer: %s\n"), dn); + } + + /* Validity. */ + { + time_t tim; + + addf (str, _("\tValidity:\n")); + + tim = gnutls_x509_crt_get_activation_time (cert); + { + char s[42]; + size_t max = sizeof (s); + struct tm t; + + if (gmtime_r (&tim, &t) == NULL) + addf (str, "error: gmtime_r (%d)\n", t); + else if (strftime (s, max, "%a %b %e %H:%M:%S UTC %Y", &t) == 0) + addf (str, "error: strftime (%d)\n", t); + else + addf (str, _("\t\tNot Before: %s\n"), s); + } + + tim = gnutls_x509_crt_get_expiration_time (cert); + { + char s[42]; + size_t max = sizeof (s); + struct tm t; + + if (gmtime_r (&tim, &t) == NULL) + addf (str, "error: gmtime_r (%d)\n", t); + else if (strftime (s, max, "%a %b %e %H:%M:%S UTC %Y", &t) == 0) + addf (str, "error: strftime (%d)\n", t); + else + addf (str, _("\t\tNot After: %s\n"), s); + } + } + + /* Subject. */ + { + char dn[1024]; + size_t dn_size = sizeof (dn); + int err; + + err = gnutls_x509_crt_get_dn (cert, dn, &dn_size); + if (err < 0) + addf (str, "error: get_dn: %s\n", gnutls_strerror (err)); + else + addf (str, _("\tSubject: %s\n"), dn); + } + + /* SubjectPublicKeyInfo. */ + { + int err; + unsigned int bits; + + err = gnutls_x509_crt_get_pk_algorithm (cert, &bits); + if (err < 0) + addf (str, "error: get_pk_algorithm: %s\n", gnutls_strerror (err)); + else + { + const char *name = gnutls_pk_algorithm_get_name (err); + if (name == NULL) + name = "Unknown"; + + addf (str, _("\tSubject Public Key Algorithm: %s\n"), name); + switch (err) + { + case GNUTLS_PK_RSA: + { + gnutls_datum_t m, e; + + err = gnutls_x509_crt_get_pk_rsa_raw (cert, &m, &e); + if (err < 0) + addf (str, "error: get_pk_rsa_raw: %s\n", + gnutls_strerror (err)); + else + { + addf (str, _("\t\tModulus (bits %d):\n"), bits); + hexdump (str, m.data, m.size, "\t\t\t"); + addf (str, _("\t\tExponent:\n")); + hexdump (str, e.data, e.size, "\t\t\t"); + } + + gnutls_free (m.data); + gnutls_free (e.data); + } + break; + + case GNUTLS_PK_DSA: + { + gnutls_datum_t p, q, g, y; + + err = gnutls_x509_crt_get_pk_dsa_raw (cert, &p, &q, &g, &y); + if (err < 0) + addf (str, "error: get_pk_dsa_raw: %s\n", + gnutls_strerror (err)); + else + { + addf (str, _("\t\tPublic key (bits %d):\n"), bits); + hexdump (str, y.data, y.size, "\t\t\t"); + addf (str, _("\t\tP:\n")); + hexdump (str, p.data, p.size, "\t\t\t"); + addf (str, _("\t\tQ:\n")); + hexdump (str, q.data, q.size, "\t\t\t"); + addf (str, _("\t\tG:\n")); + hexdump (str, g.data, g.size, "\t\t\t"); + } + } + break; + + default: + break; + } + } + } + + /* Extensions. */ + if (gnutls_x509_crt_get_version (cert) >= 3) + { + size_t i; + int err = 0; + + for (i = 0;; i++) + { + char oid[128] = ""; + size_t sizeof_oid = sizeof (oid); + int critical; + size_t san_idx = 0; + size_t proxy_idx = 0; + size_t basic_idx = 0; + size_t keyusage_idx = 0; + size_t keypurpose_idx = 0; + size_t ski_idx = 0; + size_t aki_idx = 0; + size_t crldist_idx = 0; + + err = gnutls_x509_crt_get_extension_info (cert, i, + oid, &sizeof_oid, + &critical); + if (err < 0) + { + if (err == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) + break; + addf (str, "error: get_extension_info: %s\n", + gnutls_strerror (err)); + continue; + } + + if (i == 0) + addf (str, _("\tExtensions:\n")); + + if (strcmp (oid, "2.5.29.19") == 0) + { + if (basic_idx) + { + addf (str, "error: more than one basic constraint\n"); + continue; + } + + addf (str, _("\t\tBasic Constraints (%s):\n"), + critical ? _("critical") : _("not critical")); + + print_basic (str, cert); + + basic_idx++; + } + else if (strcmp (oid, "2.5.29.14") == 0) + { + if (ski_idx) + { + addf (str, "error: more than one SKI extension\n"); + continue; + } + + addf (str, _("\t\tSubject Key Identifier (%s):\n"), + critical ? _("critical") : _("not critical")); + + print_ski (str, cert); + + ski_idx++; + } + else if (strcmp (oid, "2.5.29.35") == 0) + { + if (aki_idx) + { + addf (str, "error: more than one AKI extension\n"); + continue; + } + + addf (str, _("\t\tAuthority Key Identifier (%s):\n"), + critical ? _("critical") : _("not critical")); + + print_aki (str, cert); + + aki_idx++; + } + else if (strcmp (oid, "2.5.29.15") == 0) + { + if (keyusage_idx) + { + addf (str, "error: more than one key usage extension\n"); + continue; + } + + addf (str, _("\t\tKey Usage (%s):\n"), + critical ? _("critical") : _("not critical")); + + print_key_usage (str, cert); + + keyusage_idx++; + } + else if (strcmp (oid, "2.5.29.37") == 0) + { + if (keypurpose_idx) + { + addf (str, "error: more than one key purpose extension\n"); + continue; + } + + addf (str, _("\t\tKey Purpose (%s):\n"), + critical ? _("critical") : _("not critical")); + + print_key_purpose (str, cert); + + keypurpose_idx++; + } + else if (strcmp (oid, "2.5.29.17") == 0) + { + if (san_idx) + { + addf (str, "error: more than one SKI extension\n"); + continue; + } + + addf (str, _("\t\tSubject Alternative Name (%s):\n"), + critical ? _("critical") : _("not critical")); + + print_san (str, cert); + + san_idx++; + } + else if (strcmp (oid, "2.5.29.31") == 0) + { + if (crldist_idx) + { + addf (str, "error: more than one CRL distribution point\n"); + continue; + } + + addf (str, _("\t\tCRL Distribution points (%s):\n"), + critical ? _("critical") : _("not critical")); + + print_crldist (str, cert); + + crldist_idx++; + } + else if (strcmp (oid, "1.3.6.1.5.5.7.1.14") == 0) + { + if (proxy_idx) + { + addf (str, "error: more than one proxy extension\n"); + continue; + } + + addf (str, _("\t\tProxy Certificate Information (%s):\n"), + critical ? _("critical") : _("not critical")); + + print_proxy (str, cert); + + proxy_idx++; + } + else + { + char *buffer; + size_t extlen = 0; + + addf (str, _("\t\tUnknown extension %s (%s):\n"), oid, + critical ? _("critical") : _("not critical")); + + err = gnutls_x509_crt_get_extension_data (cert, i, + NULL, &extlen); + if (err < 0) + { + addf (str, "error: get_extension_data: %s\n", + gnutls_strerror (err)); + continue; + } + + buffer = gnutls_malloc (extlen); + if (!buffer) + { + addf (str, "error: malloc: %s\n", gnutls_strerror (err)); + continue; + } + + err = gnutls_x509_crt_get_extension_data (cert, i, + buffer, &extlen); + if (err < 0) + { + gnutls_free (buffer); + addf (str, "error: get_extension_data2: %s\n", + gnutls_strerror (err)); + continue; + } + + addf (str, _("\t\t\tASCII: ")); + asciiprint (str, buffer, extlen); + addf (str, "\n"); + + addf (str, _("\t\t\tHexdump: ")); + hexprint (str, buffer, extlen); + adds (str, "\n"); + + gnutls_free (buffer); + } + } + } + + /* Signature. */ + if (!notsigned) + { + int err; + size_t size = 0; + char *buffer = NULL; + + err = gnutls_x509_crt_get_signature_algorithm (cert); + if (err < 0) + addf (str, "error: get_signature_algorithm: %s\n", + gnutls_strerror (err)); + else + { + const char *name = gnutls_sign_algorithm_get_name (err); + if (name == NULL) + name = "Unknown"; + addf (str, _("\tSignature Algorithm: %s\n"), name); + } + if (err == GNUTLS_SIGN_RSA_MD5 || err == GNUTLS_SIGN_RSA_MD2) + { + addf (str, + _ + ("warning: signed using a broken signature algorithm that can be forged.\n")); + } + + err = gnutls_x509_crt_get_signature (cert, buffer, &size); + if (err != GNUTLS_E_SHORT_MEMORY_BUFFER) + { + addf (str, "error: get_signature: %s\n", gnutls_strerror (err)); + return; + } + + buffer = gnutls_malloc (size); + if (!buffer) + { + addf (str, "error: malloc: %s\n", gnutls_strerror (err)); + return; + } + + err = gnutls_x509_crt_get_signature (cert, buffer, &size); + if (err < 0) + { + gnutls_free (buffer); + addf (str, "error: get_signature2: %s\n", gnutls_strerror (err)); + return; + } + + addf (str, _("\tSignature:\n")); + hexdump (str, buffer, size, "\t\t"); + + gnutls_free (buffer); + } +} + +static void +print_fingerprint (gnutls_string * str, gnutls_x509_crt_t cert, + gnutls_digest_algorithm_t algo) +{ + int err; + char buffer[MAX_HASH_SIZE]; + size_t size = sizeof (buffer); + + err = gnutls_x509_crt_get_fingerprint (cert, algo, buffer, &size); + if (err < 0) + { + addf (str, "error: get_fingerprint: %s\n", gnutls_strerror (err)); + return; + } + + if (algo == GNUTLS_DIG_MD5) + addf (str, _("\tMD5 fingerprint:\n\t\t")); + else + addf (str, _("\tSHA-1 fingerprint:\n\t\t")); + hexprint (str, buffer, size); + adds (str, "\n"); +} + +static void +print_keyid (gnutls_string * str, gnutls_x509_crt_t cert) +{ + int err; + size_t size = 0; + char *buffer = NULL; + + err = gnutls_x509_crt_get_key_id (cert, 0, buffer, &size); + if (err != GNUTLS_E_SHORT_MEMORY_BUFFER) + { + addf (str, "error: get_key_id: %s\n", gnutls_strerror (err)); + return; + } + + buffer = gnutls_malloc (size); + if (!buffer) + { + addf (str, "error: malloc: %s\n", gnutls_strerror (err)); + return; + } + + err = gnutls_x509_crt_get_key_id (cert, 0, buffer, &size); + if (err < 0) + { + gnutls_free (buffer); + addf (str, "error: get_key_id2: %s\n", gnutls_strerror (err)); + return; + } + + addf (str, _("\tPublic Key Id:\n\t\t")); + hexprint (str, buffer, size); + adds (str, "\n"); + + gnutls_free (buffer); +} + +static void +print_other (gnutls_string * str, gnutls_x509_crt_t cert, int notsigned) +{ + if (!notsigned) + { + print_fingerprint (str, cert, GNUTLS_DIG_MD5); + print_fingerprint (str, cert, GNUTLS_DIG_SHA1); + } + print_keyid (str, cert); +} + +static void +print_oneline (gnutls_string * str, gnutls_x509_crt_t cert) +{ + + /* Subject. */ + { + char dn[1024]; + size_t dn_size = sizeof (dn); + int err; + + err = gnutls_x509_crt_get_dn (cert, dn, &dn_size); + if (err < 0) + addf (str, "unknown subject (%s), ", gnutls_strerror (err)); + else + addf (str, "subject `%s', ", dn); + } + + /* Issuer. */ + { + char dn[1024]; + size_t dn_size = sizeof (dn); + int err; + + err = gnutls_x509_crt_get_issuer_dn (cert, dn, &dn_size); + if (err < 0) + addf (str, "unknown issuer (%s), ", gnutls_strerror (err)); + else + addf (str, "issuer `%s', ", dn); + } + + { + int bits; + const char *name = gnutls_pk_algorithm_get_name + (gnutls_x509_crt_get_pk_algorithm (cert, &bits)); + if (name == NULL) + name = "Unknown"; + addf (str, "%s key %d bits, ", name, bits); + } + + /* Validity. */ + { + time_t tim; + + tim = gnutls_x509_crt_get_activation_time (cert); + { + char s[42]; + size_t max = sizeof (s); + struct tm t; + + if (gmtime_r (&tim, &t) == NULL) + addf (str, "unknown activation (%d), ", t); + else if (strftime (s, max, "%Y-%m-%d %H:%M:%S UTC", &t) == 0) + addf (str, "failed activation (%d), ", t); + else + addf (str, "activated `%s', ", s); + } + + tim = gnutls_x509_crt_get_expiration_time (cert); + { + char s[42]; + size_t max = sizeof (s); + struct tm t; + + if (gmtime_r (&tim, &t) == NULL) + addf (str, "unknown expiry (%d), ", t); + else if (strftime (s, max, "%Y-%m-%d %H:%M:%S UTC", &t) == 0) + addf (str, "failed expiry (%d), ", t); + else + addf (str, "expires `%s', ", s); + } + } + + { + int pathlen; + char *policyLanguage; + int err; + + err = gnutls_x509_crt_get_proxy (cert, NULL, + &pathlen, &policyLanguage, NULL, NULL); + if (err == 0) + { + addf (str, "proxy certificate (policy="); + if (strcmp (policyLanguage, "1.3.6.1.5.5.7.21.1") == 0) + addf (str, "id-ppl-inheritALL"); + else if (strcmp (policyLanguage, "1.3.6.1.5.5.7.21.2") == 0) + addf (str, "id-ppl-independent"); + else + addf (str, "%s", policyLanguage); + if (pathlen >= 0) + addf (str, ", pathlen=%d), ", pathlen); + else + addf (str, "), "); + gnutls_free (policyLanguage); + } + } + + { + char buffer[20]; + size_t size = sizeof (buffer); + int err; + + err = gnutls_x509_crt_get_fingerprint (cert, GNUTLS_DIG_SHA1, + buffer, &size); + if (err < 0) + { + addf (str, "unknown fingerprint (%s)", gnutls_strerror (err)); + } + else + { + addf (str, "SHA-1 fingerprint `"); + hexprint (str, buffer, size); + adds (str, "'"); + } + } + +} + +/** + * gnutls_x509_crt_print - Pretty print X.509 certificates + * @cert: The structure to be printed + * @format: Indicate the format to use + * @out: Newly allocated datum with zero terminated string. + * + * This function will pretty print a X.509 certificate, suitable for + * display to a human. + * + * If the format is %GNUTLS_X509_CRT_FULL then all fields of the + * certificate will be output, on multiple lines. The + * %GNUTLS_X509_CRT_ONELINE format will generate one line with some + * selected fields, which is useful for logging purposes. + * + * The output @out needs to be deallocate using gnutls_free(). + * + * Returns 0 on success. + **/ +int +gnutls_x509_crt_print (gnutls_x509_crt_t cert, + gnutls_certificate_print_formats_t format, + gnutls_datum_t * out) +{ + gnutls_string str; + + if (format == GNUTLS_X509_CRT_FULL + || format == GNUTLS_X509_CRT_UNSIGNED_FULL) + { + _gnutls_string_init (&str, gnutls_malloc, gnutls_realloc, gnutls_free); + + _gnutls_string_append_str (&str, _("X.509 Certificate Information:\n")); + + print_cert (&str, cert, format == GNUTLS_X509_CRT_UNSIGNED_FULL); + + _gnutls_string_append_str (&str, _("Other Information:\n")); + + print_other (&str, cert, format == GNUTLS_X509_CRT_UNSIGNED_FULL); + + _gnutls_string_append_data (&str, "\0", 1); + out->data = str.data; + out->size = strlen (str.data); + } + else if (format == GNUTLS_X509_CRT_ONELINE) + { + _gnutls_string_init (&str, gnutls_malloc, gnutls_realloc, gnutls_free); + + print_oneline (&str, cert); + + _gnutls_string_append_data (&str, "\0", 1); + out->data = str.data; + out->size = strlen (str.data); + } + else + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + return 0; +} + +static void +print_crl (gnutls_string * str, gnutls_x509_crl_t crl, int notsigned) +{ + /* Version. */ + { + int version = gnutls_x509_crl_get_version (crl); + if (version == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND) + addf (str, _("\tVersion: 1 (default)\n")); + else if (version < 0) + addf (str, "error: get_version: %s\n", gnutls_strerror (version)); + else + addf (str, _("\tVersion: %d\n"), version); + } + + /* Issuer. */ + if (!notsigned) + { + char dn[1024]; + size_t dn_size = sizeof (dn); + int err; + + err = gnutls_x509_crl_get_issuer_dn (crl, dn, &dn_size); + if (err < 0) + addf (str, "error: get_issuer_dn: %s\n", gnutls_strerror (err)); + else + addf (str, _("\tIssuer: %s\n"), dn); + } + + /* Validity. */ + { + time_t tim; + + addf (str, _("\tUpdate dates:\n")); + + tim = gnutls_x509_crl_get_this_update (crl); + { + char s[42]; + size_t max = sizeof (s); + struct tm t; + + if (gmtime_r (&tim, &t) == NULL) + addf (str, "error: gmtime_r (%d)\n", t); + else if (strftime (s, max, "%a %b %e %H:%M:%S UTC %Y", &t) == 0) + addf (str, "error: strftime (%d)\n", t); + else + addf (str, _("\t\tIssued: %s\n"), s); + } + + tim = gnutls_x509_crl_get_next_update (crl); + { + char s[42]; + size_t max = sizeof (s); + struct tm t; + + if (tim == -1) + addf (str, "\t\tNo next update time.\n"); + else if (gmtime_r (&tim, &t) == NULL) + addf (str, "error: gmtime_r (%d)\n", t); + else if (strftime (s, max, "%a %b %e %H:%M:%S UTC %Y", &t) == 0) + addf (str, "error: strftime (%d)\n", t); + else + addf (str, _("\t\tNext at: %s\n"), s); + } + } + + /* Revoked certificates. */ + { + int num = gnutls_x509_crl_get_crt_count (crl); + int j; + + if (num) + addf (str, _("\tRevoked certificates (%d):\n"), num); + else + addf (str, _("\tNo revoked certificates.\n")); + + for (j = 0; j < num; j++) + { + char serial[128]; + size_t serial_size = sizeof (serial); + int err; + time_t tim; + + err = gnutls_x509_crl_get_crt_serial (crl, j, serial, + &serial_size, &tim); + if (err < 0) + addf (str, "error: get_crt_serial: %s\n", gnutls_strerror (err)); + else + { + char s[42]; + size_t max = sizeof (s); + struct tm t; + + addf (str, _("\t\tSerial Number (hex): ")); + hexprint (str, serial, serial_size); + adds (str, "\n"); + + if (gmtime_r (&tim, &t) == NULL) + addf (str, "error: gmtime_r (%d)\n", t); + else if (strftime (s, max, "%a %b %e %H:%M:%S UTC %Y", &t) == 0) + addf (str, "error: strftime (%d)\n", t); + else + addf (str, _("\t\tRevoked at: %s\n"), s); + } + } + } + + /* Signature. */ + if (!notsigned) + { + int err; + size_t size = 0; + char *buffer = NULL; + + err = gnutls_x509_crl_get_signature_algorithm (crl); + if (err < 0) + addf (str, "error: get_signature_algorithm: %s\n", + gnutls_strerror (err)); + else + { + const char *name = gnutls_sign_algorithm_get_name (err); + if (name == NULL) + name = "Unknown"; + addf (str, _("\tSignature Algorithm: %s\n"), name); + } + if (err == GNUTLS_SIGN_RSA_MD5 || err == GNUTLS_SIGN_RSA_MD2) + { + addf (str, + _ + ("warning: signed using a broken signature algorithm that can be forged.\n")); + } + + err = gnutls_x509_crl_get_signature (crl, buffer, &size); + if (err != GNUTLS_E_SHORT_MEMORY_BUFFER) + { + addf (str, "error: get_signature: %s\n", gnutls_strerror (err)); + return; + } + + buffer = gnutls_malloc (size); + if (!buffer) + { + addf (str, "error: malloc: %s\n", gnutls_strerror (err)); + return; + } + + err = gnutls_x509_crl_get_signature (crl, buffer, &size); + if (err < 0) + { + gnutls_free (buffer); + addf (str, "error: get_signature2: %s\n", gnutls_strerror (err)); + return; + } + + addf (str, _("\tSignature:\n")); + hexdump (str, buffer, size, "\t\t"); + + gnutls_free (buffer); + } +} + +/** + * gnutls_x509_crl_print - Pretty print X.509 certificate revocation list + * @crl: The structure to be printed + * @format: Indicate the format to use + * @out: Newly allocated datum with zero terminated string. + * + * This function will pretty print a X.509 certificate revocation + * list, suitable for display to a human. + * + * The output @out needs to be deallocate using gnutls_free(). + * + * Returns 0 on success. + **/ +int +gnutls_x509_crl_print (gnutls_x509_crl_t crl, + gnutls_certificate_print_formats_t format, + gnutls_datum_t * out) +{ + gnutls_string str; + + _gnutls_string_init (&str, gnutls_malloc, gnutls_realloc, gnutls_free); + + _gnutls_string_append_str + (&str, _("X.509 Certificate Revocation List Information:\n")); + + print_crl (&str, crl, format == GNUTLS_X509_CRT_UNSIGNED_FULL); + + _gnutls_string_append_data (&str, "\0", 1); + out->data = str.data; + out->size = strlen (str.data); + + return 0; +} diff --git a/src/daemon/https/x509/pkcs12.c b/src/daemon/https/x509/pkcs12.c new file mode 100644 index 00000000..40f7a243 --- /dev/null +++ b/src/daemon/https/x509/pkcs12.c @@ -0,0 +1,1325 @@ +/* + * Copyright (C) 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +/* Functions that relate on PKCS12 packet parsing. + */ + +#include +#include + +#ifdef ENABLE_PKI + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* Decodes the PKCS #12 auth_safe, and returns the allocated raw data, + * which holds them. Returns an ASN1_TYPE of authenticatedSafe. + */ +static int +_decode_pkcs12_auth_safe (ASN1_TYPE pkcs12, ASN1_TYPE * authen_safe, + gnutls_datum_t * raw) +{ + char oid[128]; + ASN1_TYPE c2 = ASN1_TYPE_EMPTY; + gnutls_datum_t auth_safe = { NULL, 0 }; + int tmp_size, len, result; + + len = sizeof (oid) - 1; + result = asn1_read_value (pkcs12, "authSafe.contentType", oid, &len); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + if (strcmp (oid, DATA_OID) != 0) + { + gnutls_assert (); + _gnutls_x509_log ("Unknown PKCS12 Content OID '%s'\n", oid); + return GNUTLS_E_UNKNOWN_PKCS_CONTENT_TYPE; + } + + /* Step 1. Read the content data + */ + + tmp_size = 0; + result = + _gnutls_x509_read_value (pkcs12, "authSafe.content", &auth_safe, 1); + if (result < 0) + { + gnutls_assert (); + goto cleanup; + } + + /* Step 2. Extract the authenticatedSafe. + */ + + if ((result = asn1_create_element + (_gnutls_get_pkix (), "PKIX1.pkcs-12-AuthenticatedSafe", + &c2)) != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + result = asn1_der_decoding (&c2, auth_safe.data, auth_safe.size, NULL); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + if (raw == NULL) + { + _gnutls_free_datum (&auth_safe); + } + else + { + raw->data = auth_safe.data; + raw->size = auth_safe.size; + } + + if (authen_safe) + *authen_safe = c2; + else + asn1_delete_structure (&c2); + + return 0; + +cleanup: + if (c2) + asn1_delete_structure (&c2); + _gnutls_free_datum (&auth_safe); + return result; +} + +/** + * gnutls_pkcs12_init - This function initializes a gnutls_pkcs12_t structure + * @pkcs12: The structure to be initialized + * + * This function will initialize a PKCS12 structure. PKCS12 structures + * usually contain lists of X.509 Certificates and X.509 Certificate + * revocation lists. + * + * Returns 0 on success. + * + **/ +int +gnutls_pkcs12_init (gnutls_pkcs12_t * pkcs12) +{ + *pkcs12 = gnutls_calloc (1, sizeof (gnutls_pkcs12_int)); + + if (*pkcs12) + { + int result = asn1_create_element (_gnutls_get_pkix (), + "PKIX1.pkcs-12-PFX", + &(*pkcs12)->pkcs12); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + gnutls_free (*pkcs12); + return _gnutls_asn2err (result); + } + return 0; /* success */ + } + return GNUTLS_E_MEMORY_ERROR; +} + +/** + * gnutls_pkcs12_deinit - This function deinitializes memory used by a gnutls_pkcs12_t structure + * @pkcs12: The structure to be initialized + * + * This function will deinitialize a PKCS12 structure. + * + **/ +void +gnutls_pkcs12_deinit (gnutls_pkcs12_t pkcs12) +{ + if (!pkcs12) + return; + + if (pkcs12->pkcs12) + asn1_delete_structure (&pkcs12->pkcs12); + + gnutls_free (pkcs12); +} + +/** + * gnutls_pkcs12_import - This function will import a DER or PEM encoded PKCS12 structure + * @pkcs12: The structure to store the parsed PKCS12. + * @data: The DER or PEM encoded PKCS12. + * @format: One of DER or PEM + * @flags: an ORed sequence of gnutls_privkey_pkcs8_flags + * + * This function will convert the given DER or PEM encoded PKCS12 + * to the native gnutls_pkcs12_t format. The output will be stored in 'pkcs12'. + * + * If the PKCS12 is PEM encoded it should have a header of "PKCS12". + * + * Returns 0 on success. + * + **/ +int +gnutls_pkcs12_import (gnutls_pkcs12_t pkcs12, + const gnutls_datum_t * data, + gnutls_x509_crt_fmt_t format, unsigned int flags) +{ + int result = 0, need_free = 0; + gnutls_datum_t _data; + + _data.data = data->data; + _data.size = data->size; + + if (pkcs12 == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + /* If the PKCS12 is in PEM format then decode it + */ + if (format == GNUTLS_X509_FMT_PEM) + { + opaque *out; + + result = _gnutls_fbase64_decode (PEM_PKCS12, data->data, data->size, + &out); + + if (result <= 0) + { + if (result == 0) + result = GNUTLS_E_INTERNAL_ERROR; + gnutls_assert (); + return result; + } + + _data.data = out; + _data.size = result; + + need_free = 1; + } + + result = asn1_der_decoding (&pkcs12->pkcs12, _data.data, _data.size, NULL); + if (result != ASN1_SUCCESS) + { + result = _gnutls_asn2err (result); + gnutls_assert (); + goto cleanup; + } + + if (need_free) + _gnutls_free_datum (&_data); + + return 0; + +cleanup: + if (need_free) + _gnutls_free_datum (&_data); + return result; +} + + +/** + * gnutls_pkcs12_export - This function will export the pkcs12 structure + * @pkcs12: Holds the pkcs12 structure + * @format: the format of output params. One of PEM or DER. + * @output_data: will contain a structure PEM or DER encoded + * @output_data_size: holds the size of output_data (and will be + * replaced by the actual size of parameters) + * + * This function will export the pkcs12 structure to DER or PEM format. + * + * If the buffer provided is not long enough to hold the output, then + * *output_data_size will be updated and GNUTLS_E_SHORT_MEMORY_BUFFER + * will be returned. + * + * If the structure is PEM encoded, it will have a header + * of "BEGIN PKCS12". + * + * Return value: In case of failure a negative value will be + * returned, and 0 on success. + * + **/ +int +gnutls_pkcs12_export (gnutls_pkcs12_t pkcs12, + gnutls_x509_crt_fmt_t format, void *output_data, + size_t * output_data_size) +{ + if (pkcs12 == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_export_int (pkcs12->pkcs12, format, PEM_PKCS12, + output_data, output_data_size); +} + +static int +oid2bag (const char *oid) +{ + if (strcmp (oid, BAG_PKCS8_KEY) == 0) + return GNUTLS_BAG_PKCS8_KEY; + if (strcmp (oid, BAG_PKCS8_ENCRYPTED_KEY) == 0) + return GNUTLS_BAG_PKCS8_ENCRYPTED_KEY; + if (strcmp (oid, BAG_CERTIFICATE) == 0) + return GNUTLS_BAG_CERTIFICATE; + if (strcmp (oid, BAG_CRL) == 0) + return GNUTLS_BAG_CRL; + + return GNUTLS_BAG_UNKNOWN; +} + +static const char * +bag_to_oid (int bag) +{ + switch (bag) + { + case GNUTLS_BAG_PKCS8_KEY: + return BAG_PKCS8_KEY; + case GNUTLS_BAG_PKCS8_ENCRYPTED_KEY: + return BAG_PKCS8_ENCRYPTED_KEY; + case GNUTLS_BAG_CERTIFICATE: + return BAG_CERTIFICATE; + case GNUTLS_BAG_CRL: + return BAG_CRL; + } + return NULL; +} + +static inline char * +ucs2_to_ascii (char *data, int size) +{ + int i, j; + + for (i = 0; i < size / 2; i++) + { + j = 2 * i + 1; + if (isascii (data[j])) + data[i] = data[i * 2 + 1]; + else + data[i] = '?'; + } + data[i] = 0; + + return data; +} + +/* Decodes the SafeContents, and puts the output in + * the given bag. + */ +int +_pkcs12_decode_safe_contents (const gnutls_datum_t * content, + gnutls_pkcs12_bag_t bag) +{ + char oid[128], root[MAX_NAME_SIZE]; + ASN1_TYPE c2 = ASN1_TYPE_EMPTY; + int len, result; + int bag_type; + gnutls_datum_t attr_val; + int count = 0, i, attributes, j; + size_t size; + + /* Step 1. Extract the SEQUENCE. + */ + + if ((result = asn1_create_element + (_gnutls_get_pkix (), "PKIX1.pkcs-12-SafeContents", + &c2)) != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + result = asn1_der_decoding (&c2, content->data, content->size, NULL); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + /* Count the number of bags + */ + result = asn1_number_of_elements (c2, "", &count); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + bag->bag_elements = MIN (MAX_BAG_ELEMENTS, count); + + for (i = 0; i < bag->bag_elements; i++) + { + + snprintf (root, sizeof (root), "?%u.bagId", i + 1); + + len = sizeof (oid); + result = asn1_read_value (c2, root, oid, &len); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + /* Read the Bag type + */ + bag_type = oid2bag (oid); + + if (bag_type < 0) + { + gnutls_assert (); + goto cleanup; + } + + /* Read the Bag Value + */ + + snprintf (root, sizeof (root), "?%u.bagValue", i + 1); + + result = _gnutls_x509_read_value (c2, root, &bag->element[i].data, 0); + if (result < 0) + { + gnutls_assert (); + goto cleanup; + } + + if (bag_type == GNUTLS_BAG_CERTIFICATE || bag_type == GNUTLS_BAG_CRL) + { + gnutls_datum_t tmp = bag->element[i].data; + + result = + _pkcs12_decode_crt_bag (bag_type, &tmp, &bag->element[i].data); + if (result < 0) + { + gnutls_assert (); + goto cleanup; + } + + _gnutls_free_datum (&tmp); + } + + /* read the bag attributes + */ + snprintf (root, sizeof (root), "?%u.bagAttributes", i + 1); + + result = asn1_number_of_elements (c2, root, &attributes); + if (result != ASN1_SUCCESS && result != ASN1_ELEMENT_NOT_FOUND) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + if (attributes < 0) + attributes = 1; + + if (result != ASN1_ELEMENT_NOT_FOUND) + for (j = 0; j < attributes; j++) + { + + snprintf (root, sizeof (root), "?%u.bagAttributes.?%u", i + 1, + j + 1); + + result = + _gnutls_x509_decode_and_read_attribute (c2, root, oid, + sizeof (oid), &attr_val, + 1, 0); + + if (result < 0) + { + gnutls_assert (); + continue; /* continue in case we find some known attributes */ + } + + if (strcmp (oid, KEY_ID_OID) == 0) + { + size = attr_val.size; + + result = + _gnutls_x509_decode_octet_string (NULL, attr_val.data, size, + attr_val.data, &size); + attr_val.size = size; + if (result < 0) + { + _gnutls_free_datum (&attr_val); + gnutls_assert (); + _gnutls_x509_log + ("Error decoding PKCS12 Bag Attribute OID '%s'\n", oid); + continue; + } + bag->element[i].local_key_id = attr_val; + } + else if (strcmp (oid, FRIENDLY_NAME_OID) == 0) + { + size = attr_val.size; + result = + _gnutls_x509_decode_octet_string ("BMPString", + attr_val.data, size, + attr_val.data, &size); + attr_val.size = size; + if (result < 0) + { + _gnutls_free_datum (&attr_val); + gnutls_assert (); + _gnutls_x509_log + ("Error decoding PKCS12 Bag Attribute OID '%s'\n", oid); + continue; + } + bag->element[i].friendly_name = + ucs2_to_ascii (attr_val.data, attr_val.size); + } + else + { + _gnutls_free_datum (&attr_val); + _gnutls_x509_log + ("Unknown PKCS12 Bag Attribute OID '%s'\n", oid); + } + } + + + bag->element[i].type = bag_type; + + } + + asn1_delete_structure (&c2); + + + return 0; + +cleanup: + if (c2) + asn1_delete_structure (&c2); + return result; + +} + + +static int +_parse_safe_contents (ASN1_TYPE sc, const char *sc_name, + gnutls_pkcs12_bag_t bag) +{ + gnutls_datum_t content = { NULL, 0 }; + int result; + + /* Step 1. Extract the content. + */ + + result = _gnutls_x509_read_value (sc, sc_name, &content, 1); + if (result < 0) + { + gnutls_assert (); + goto cleanup; + } + + result = _pkcs12_decode_safe_contents (&content, bag); + if (result < 0) + { + gnutls_assert (); + goto cleanup; + } + + _gnutls_free_datum (&content); + + return 0; + +cleanup: + _gnutls_free_datum (&content); + return result; +} + + +/** + * gnutls_pkcs12_get_bag - This function returns a Bag from a PKCS12 structure + * @pkcs12: should contain a gnutls_pkcs12_t structure + * @indx: contains the index of the bag to extract + * @bag: An initialized bag, where the contents of the bag will be copied + * + * This function will return a Bag from the PKCS12 structure. + * Returns 0 on success. + * + * After the last Bag has been read GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE + * will be returned. + * + **/ +int +gnutls_pkcs12_get_bag (gnutls_pkcs12_t pkcs12, + int indx, gnutls_pkcs12_bag_t bag) +{ + ASN1_TYPE c2 = ASN1_TYPE_EMPTY; + int result, len; + char root2[MAX_NAME_SIZE]; + char oid[128]; + + if (pkcs12 == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + /* Step 1. decode the data. + */ + result = _decode_pkcs12_auth_safe (pkcs12->pkcs12, &c2, NULL); + if (result < 0) + { + gnutls_assert (); + return result; + } + + /* Step 2. Parse the AuthenticatedSafe + */ + + snprintf (root2, sizeof (root2), "?%u.contentType", indx + 1); + + len = sizeof (oid) - 1; + result = asn1_read_value (c2, root2, oid, &len); + + if (result == ASN1_ELEMENT_NOT_FOUND) + { + result = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + goto cleanup; + } + + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + /* Not encrypted Bag + */ + + snprintf (root2, sizeof (root2), "?%u.content", indx + 1); + + if (strcmp (oid, DATA_OID) == 0) + { + result = _parse_safe_contents (c2, root2, bag); + goto cleanup; + } + + /* ENC_DATA_OID needs decryption */ + + bag->element[0].type = GNUTLS_BAG_ENCRYPTED; + bag->bag_elements = 1; + + result = _gnutls_x509_read_value (c2, root2, &bag->element[0].data, 0); + if (result < 0) + { + gnutls_assert (); + goto cleanup; + } + + result = 0; + +cleanup: + if (c2) + asn1_delete_structure (&c2); + return result; +} + +/* Creates an empty PFX structure for the PKCS12 structure. + */ +static int +create_empty_pfx (ASN1_TYPE pkcs12) +{ + uint8_t three = 3; + int result; + ASN1_TYPE c2 = ASN1_TYPE_EMPTY; + + /* Use version 3 + */ + result = asn1_write_value (pkcs12, "version", &three, 1); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + /* Write the content type of the data + */ + result = asn1_write_value (pkcs12, "authSafe.contentType", DATA_OID, 1); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + /* Check if the authenticatedSafe content is empty, and encode a + * null one in that case. + */ + + if ((result = asn1_create_element + (_gnutls_get_pkix (), "PKIX1.pkcs-12-AuthenticatedSafe", + &c2)) != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + result = + _gnutls_x509_der_encode_and_copy (c2, "", pkcs12, "authSafe.content", 1); + if (result < 0) + { + gnutls_assert (); + goto cleanup; + } + asn1_delete_structure (&c2); + + return 0; + +cleanup: + asn1_delete_structure (&c2); + return result; + +} + +/** + * gnutls_pkcs12_set_bag - This function inserts a Bag into a PKCS12 structure + * @pkcs12: should contain a gnutls_pkcs12_t structure + * @bag: An initialized bag + * + * This function will insert a Bag into the PKCS12 structure. + * Returns 0 on success. + * + **/ +int +gnutls_pkcs12_set_bag (gnutls_pkcs12_t pkcs12, gnutls_pkcs12_bag_t bag) +{ + ASN1_TYPE c2 = ASN1_TYPE_EMPTY; + ASN1_TYPE safe_cont = ASN1_TYPE_EMPTY; + int result; + int enc = 0, dum = 1; + char null; + + if (pkcs12 == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + /* Step 1. Check if the pkcs12 structure is empty. In that + * case generate an empty PFX. + */ + result = asn1_read_value (pkcs12->pkcs12, "authSafe.content", &null, &dum); + if (result == ASN1_VALUE_NOT_FOUND) + { + result = create_empty_pfx (pkcs12->pkcs12); + if (result < 0) + { + gnutls_assert (); + return result; + } + } + + /* Step 2. decode the authenticatedSafe. + */ + result = _decode_pkcs12_auth_safe (pkcs12->pkcs12, &c2, NULL); + if (result < 0) + { + gnutls_assert (); + return result; + } + + /* Step 3. Encode the bag elements into a SafeContents + * structure. + */ + result = _pkcs12_encode_safe_contents (bag, &safe_cont, &enc); + if (result < 0) + { + gnutls_assert (); + return result; + } + + /* Step 4. Insert the encoded SafeContents into the AuthenticatedSafe + * structure. + */ + result = asn1_write_value (c2, "", "NEW", 1); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + if (enc) + result = asn1_write_value (c2, "?LAST.contentType", ENC_DATA_OID, 1); + else + result = asn1_write_value (c2, "?LAST.contentType", DATA_OID, 1); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + if (enc) + { + /* Encrypted packets are written directly. + */ + result = + asn1_write_value (c2, "?LAST.content", + bag->element[0].data.data, + bag->element[0].data.size); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + } + else + { + result = + _gnutls_x509_der_encode_and_copy (safe_cont, "", c2, + "?LAST.content", 1); + if (result < 0) + { + gnutls_assert (); + goto cleanup; + } + } + + asn1_delete_structure (&safe_cont); + + + /* Step 5. Reencode and copy the AuthenticatedSafe into the pkcs12 + * structure. + */ + result = + _gnutls_x509_der_encode_and_copy (c2, "", pkcs12->pkcs12, + "authSafe.content", 1); + if (result < 0) + { + gnutls_assert (); + goto cleanup; + } + + asn1_delete_structure (&c2); + + return 0; + +cleanup: + asn1_delete_structure (&c2); + asn1_delete_structure (&safe_cont); + return result; +} + +/** + * gnutls_pkcs12_generate_mac - This function generates the MAC of the PKCS12 structure + * @pkcs12: should contain a gnutls_pkcs12_t structure + * @pass: The password for the MAC + * + * This function will generate a MAC for the PKCS12 structure. + * Returns 0 on success. + * + **/ +int +gnutls_pkcs12_generate_mac (gnutls_pkcs12_t pkcs12, const char *pass) +{ + opaque salt[8], key[20]; + int result; + const int iter = 1; + mac_hd_t td1 = NULL; + gnutls_datum_t tmp = { NULL, 0 }; + opaque sha_mac[20]; + + if (pkcs12 == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + /* Generate the salt. + */ + if (gc_nonce (salt, sizeof (salt)) != GC_OK) + { + gnutls_assert (); + return GNUTLS_E_RANDOM_FAILED; + } + + /* Write the salt into the structure. + */ + result = + asn1_write_value (pkcs12->pkcs12, "macData.macSalt", salt, sizeof (salt)); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + /* write the iterations + */ + + if (iter > 1) + { + result = + _gnutls_x509_write_uint32 (pkcs12->pkcs12, "macData.iterations", + iter); + if (result < 0) + { + gnutls_assert (); + goto cleanup; + } + } + + /* Generate the key. + */ + result = _pkcs12_string_to_key (3 /*MAC*/, salt, sizeof (salt), + iter, pass, sizeof (key), key); + if (result < 0) + { + gnutls_assert (); + goto cleanup; + } + + /* Get the data to be MACed + */ + result = _decode_pkcs12_auth_safe (pkcs12->pkcs12, NULL, &tmp); + if (result < 0) + { + gnutls_assert (); + goto cleanup; + } + + /* MAC the data + */ + td1 = _gnutls_hmac_init (GNUTLS_MAC_SHA1, key, sizeof (key)); + if (td1 == GNUTLS_MAC_FAILED) + { + gnutls_assert (); + result = GNUTLS_E_INTERNAL_ERROR; + goto cleanup; + } + + _gnutls_hmac (td1, tmp.data, tmp.size); + _gnutls_free_datum (&tmp); + + _gnutls_hmac_deinit (td1, sha_mac); + + + result = + asn1_write_value (pkcs12->pkcs12, "macData.mac.digest", sha_mac, + sizeof (sha_mac)); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + result = + asn1_write_value (pkcs12->pkcs12, + "macData.mac.digestAlgorithm.parameters", NULL, 0); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + result = + asn1_write_value (pkcs12->pkcs12, + "macData.mac.digestAlgorithm.algorithm", HASH_OID_SHA1, + 1); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + return 0; + +cleanup: + _gnutls_free_datum (&tmp); + return result; +} + +/** + * gnutls_pkcs12_verify_mac - This function verifies the MAC of the PKCS12 structure + * @pkcs12: should contain a gnutls_pkcs12_t structure + * @pass: The password for the MAC + * + * This function will verify the MAC for the PKCS12 structure. + * Returns 0 on success. + * + **/ +int +gnutls_pkcs12_verify_mac (gnutls_pkcs12_t pkcs12, const char *pass) +{ + opaque key[20]; + int result; + unsigned int iter; + int len; + mac_hd_t td1 = NULL; + gnutls_datum_t tmp = { NULL, 0 }, salt = + { + NULL, 0}; + opaque sha_mac[20]; + opaque sha_mac_orig[20]; + + if (pkcs12 == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + /* read the iterations + */ + + result = + _gnutls_x509_read_uint (pkcs12->pkcs12, "macData.iterations", &iter); + if (result < 0) + { + iter = 1; /* the default */ + } + + + /* Read the salt from the structure. + */ + result = + _gnutls_x509_read_value (pkcs12->pkcs12, "macData.macSalt", &salt, 0); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + /* Generate the key. + */ + result = _pkcs12_string_to_key (3 /*MAC*/, salt.data, salt.size, + iter, pass, sizeof (key), key); + if (result < 0) + { + gnutls_assert (); + goto cleanup; + } + + _gnutls_free_datum (&salt); + + /* Get the data to be MACed + */ + result = _decode_pkcs12_auth_safe (pkcs12->pkcs12, NULL, &tmp); + if (result < 0) + { + gnutls_assert (); + goto cleanup; + } + + /* MAC the data + */ + td1 = _gnutls_hmac_init (GNUTLS_MAC_SHA1, key, sizeof (key)); + if (td1 == GNUTLS_MAC_FAILED) + { + gnutls_assert (); + result = GNUTLS_E_INTERNAL_ERROR; + goto cleanup; + } + + _gnutls_hmac (td1, tmp.data, tmp.size); + _gnutls_free_datum (&tmp); + + _gnutls_hmac_deinit (td1, sha_mac); + + len = sizeof (sha_mac_orig); + result = + asn1_read_value (pkcs12->pkcs12, "macData.mac.digest", sha_mac_orig, + &len); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + if (memcmp (sha_mac_orig, sha_mac, sizeof (sha_mac)) != 0) + { + gnutls_assert (); + return GNUTLS_E_MAC_VERIFY_FAILED; + } + + return 0; + +cleanup: + _gnutls_free_datum (&tmp); + _gnutls_free_datum (&salt); + return result; +} + + +static int +write_attributes (gnutls_pkcs12_bag_t bag, int elem, + ASN1_TYPE c2, const char *where) +{ + int result; + char root[128]; + + /* If the bag attributes are empty, then write + * nothing to the attribute field. + */ + if (bag->element[elem].friendly_name == NULL && + bag->element[elem].local_key_id.data == NULL) + { + /* no attributes + */ + result = asn1_write_value (c2, where, NULL, 0); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + return 0; + } + + if (bag->element[elem].local_key_id.data != NULL) + { + + /* Add a new Attribute + */ + result = asn1_write_value (c2, where, "NEW", 1); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + _gnutls_str_cpy (root, sizeof (root), where); + _gnutls_str_cat (root, sizeof (root), ".?LAST"); + + result = + _gnutls_x509_encode_and_write_attribute (KEY_ID_OID, c2, root, + bag->element[elem]. + local_key_id.data, + bag->element[elem]. + local_key_id.size, 1); + if (result < 0) + { + gnutls_assert (); + return result; + } + } + + if (bag->element[elem].friendly_name != NULL) + { + opaque *name; + int size, i; + const char *p; + + /* Add a new Attribute + */ + result = asn1_write_value (c2, where, "NEW", 1); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + /* convert name to BMPString + */ + size = strlen (bag->element[elem].friendly_name) * 2; + name = gnutls_malloc (size); + + if (name == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + p = bag->element[elem].friendly_name; + for (i = 0; i < size; i += 2) + { + name[i] = 0; + name[i + 1] = *p; + p++; + } + + _gnutls_str_cpy (root, sizeof (root), where); + _gnutls_str_cat (root, sizeof (root), ".?LAST"); + + result = + _gnutls_x509_encode_and_write_attribute (FRIENDLY_NAME_OID, c2, + root, name, size, 1); + + gnutls_free (name); + + if (result < 0) + { + gnutls_assert (); + return result; + } + } + + return 0; +} + + +/* Encodes the bag into a SafeContents structure, and puts the output in + * the given datum. Enc is set to non zero if the data are encrypted; + */ +int +_pkcs12_encode_safe_contents (gnutls_pkcs12_bag_t bag, ASN1_TYPE * contents, + int *enc) +{ + ASN1_TYPE c2 = ASN1_TYPE_EMPTY; + int result; + int i; + const char *oid; + + if (bag->element[0].type == GNUTLS_BAG_ENCRYPTED && enc) + { + *enc = 1; + return 0; /* ENCRYPTED BAG, do nothing. */ + } + else if (enc) + *enc = 0; + + /* Step 1. Create the SEQUENCE. + */ + + if ((result = asn1_create_element + (_gnutls_get_pkix (), "PKIX1.pkcs-12-SafeContents", + &c2)) != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + for (i = 0; i < bag->bag_elements; i++) + { + + oid = bag_to_oid (bag->element[i].type); + if (oid == NULL) + { + gnutls_assert (); + continue; + } + + result = asn1_write_value (c2, "", "NEW", 1); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + /* Copy the bag type. + */ + result = asn1_write_value (c2, "?LAST.bagId", oid, 1); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + /* Set empty attributes + */ + result = write_attributes (bag, i, c2, "?LAST.bagAttributes"); + if (result < 0) + { + gnutls_assert (); + goto cleanup; + } + + + /* Copy the Bag Value + */ + + if (bag->element[i].type == GNUTLS_BAG_CERTIFICATE || + bag->element[i].type == GNUTLS_BAG_CRL) + { + gnutls_datum_t tmp; + + /* in that case encode it to a CertBag or + * a CrlBag. + */ + + result = + _pkcs12_encode_crt_bag (bag->element[i].type, + &bag->element[i].data, &tmp); + + if (result < 0) + { + gnutls_assert (); + goto cleanup; + } + + result = _gnutls_x509_write_value (c2, "?LAST.bagValue", &tmp, 0); + + _gnutls_free_datum (&tmp); + + } + else + { + + result = _gnutls_x509_write_value (c2, "?LAST.bagValue", + &bag->element[i].data, 0); + } + + if (result < 0) + { + gnutls_assert (); + goto cleanup; + } + + } + + /* Encode the data and copy them into the datum + */ + *contents = c2; + + return 0; + +cleanup: + if (c2) + asn1_delete_structure (&c2); + return result; + +} + + +#endif /* ENABLE_PKI */ diff --git a/src/daemon/https/x509/pkcs12.h b/src/daemon/https/x509/pkcs12.h new file mode 100644 index 00000000..566058fb --- /dev/null +++ b/src/daemon/https/x509/pkcs12.h @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +// TODO clean + +#ifndef GNUTLS_PKCS12_H +# define GNUTLS_PKCS12_H + +#ifdef __cplusplus +extern "C" + { +#endif + +#include + +#define MAX_BAG_ELEMENTS 32 + +/* PKCS12 structures handling + */ +struct gnutls_pkcs12_int; + +struct gnutls_pkcs12_bag_int; +typedef struct gnutls_pkcs12_int + { + ASN1_TYPE pkcs12; + } gnutls_pkcs12_int; + +typedef enum gnutls_pkcs12_bag_type_t + { + GNUTLS_BAG_EMPTY = 0, + + GNUTLS_BAG_PKCS8_ENCRYPTED_KEY = 1, + GNUTLS_BAG_PKCS8_KEY, + GNUTLS_BAG_CERTIFICATE, + GNUTLS_BAG_CRL, + GNUTLS_BAG_ENCRYPTED = 10, + GNUTLS_BAG_UNKNOWN = 20 + } gnutls_pkcs12_bag_type_t; + +struct bag_element + { + gnutls_datum_t data; + gnutls_pkcs12_bag_type_t type; + gnutls_datum_t local_key_id; + char *friendly_name; + }; + +typedef struct gnutls_pkcs12_bag_int + { + struct bag_element element[MAX_BAG_ELEMENTS]; + int bag_elements; + } gnutls_pkcs12_bag_int; + +/* Bag attributes */ +#define FRIENDLY_NAME_OID "1.2.840.113549.1.9.20" +#define KEY_ID_OID "1.2.840.113549.1.9.21" + +typedef struct gnutls_pkcs12_int *gnutls_pkcs12_t; +typedef struct gnutls_pkcs12_bag_int *gnutls_pkcs12_bag_t; + +int gnutls_pkcs12_init(gnutls_pkcs12_t * pkcs12); +void gnutls_pkcs12_deinit(gnutls_pkcs12_t pkcs12); +int gnutls_pkcs12_import(gnutls_pkcs12_t pkcs12, + const gnutls_datum_t * data, + gnutls_x509_crt_fmt_t format, + unsigned int flags); +int gnutls_pkcs12_export(gnutls_pkcs12_t pkcs12, + gnutls_x509_crt_fmt_t format, + void *output_data, + size_t * output_data_size); + +int gnutls_pkcs12_get_bag(gnutls_pkcs12_t pkcs12, + int indx, + gnutls_pkcs12_bag_t bag); +int gnutls_pkcs12_set_bag(gnutls_pkcs12_t pkcs12, + gnutls_pkcs12_bag_t bag); + +int gnutls_pkcs12_generate_mac(gnutls_pkcs12_t pkcs12, + const char *pass); +int gnutls_pkcs12_verify_mac(gnutls_pkcs12_t pkcs12, + const char *pass); + +int gnutls_pkcs12_bag_decrypt(gnutls_pkcs12_bag_t bag, + const char *pass); +int gnutls_pkcs12_bag_encrypt(gnutls_pkcs12_bag_t bag, + const char *pass, + unsigned int flags); + +gnutls_pkcs12_bag_type_t gnutls_pkcs12_bag_get_type(gnutls_pkcs12_bag_t + bag, + int indx); +int gnutls_pkcs12_bag_get_data(gnutls_pkcs12_bag_t bag, + int indx, + gnutls_datum_t * data); +int gnutls_pkcs12_bag_set_data(gnutls_pkcs12_bag_t bag, + gnutls_pkcs12_bag_type_t type, + const gnutls_datum_t * data); +int gnutls_pkcs12_bag_set_crl(gnutls_pkcs12_bag_t bag, + gnutls_x509_crl_t crl); +int gnutls_pkcs12_bag_set_crt(gnutls_pkcs12_bag_t bag, + gnutls_x509_crt_t crt); + +int gnutls_pkcs12_bag_init(gnutls_pkcs12_bag_t * bag); +void gnutls_pkcs12_bag_deinit(gnutls_pkcs12_bag_t bag); +int gnutls_pkcs12_bag_get_count(gnutls_pkcs12_bag_t bag); + +int gnutls_pkcs12_bag_get_key_id(gnutls_pkcs12_bag_t bag, + int indx, + gnutls_datum_t * id); +int gnutls_pkcs12_bag_set_key_id(gnutls_pkcs12_bag_t bag, + int indx, + const gnutls_datum_t * id); + +int gnutls_pkcs12_bag_get_friendly_name(gnutls_pkcs12_bag_t bag, + int indx, + char **name); +int gnutls_pkcs12_bag_set_friendly_name(gnutls_pkcs12_bag_t bag, + int indx, + const char *name); + +#ifdef __cplusplus +} +#endif + +#define BAG_PKCS8_KEY "1.2.840.113549.1.12.10.1.1" +#define BAG_PKCS8_ENCRYPTED_KEY "1.2.840.113549.1.12.10.1.2" +#define BAG_CERTIFICATE "1.2.840.113549.1.12.10.1.3" +#define BAG_CRL "1.2.840.113549.1.12.10.1.4" + +/* PKCS #7 + */ +#define DATA_OID "1.2.840.113549.1.7.1" +#define ENC_DATA_OID "1.2.840.113549.1.7.6" + +int gnutls_pkcs12_init(gnutls_pkcs12_t * pkcs12); +void gnutls_pkcs12_deinit(gnutls_pkcs12_t pkcs12); +int gnutls_pkcs12_import(gnutls_pkcs12_t pkcs12, + const gnutls_datum_t * data, + gnutls_x509_crt_fmt_t format, + unsigned int flags); + +int gnutls_pkcs12_get_bag(gnutls_pkcs12_t pkcs12, + int indx, + gnutls_pkcs12_bag_t bag); + +int gnutls_pkcs12_bag_init(gnutls_pkcs12_bag_t * bag); +void gnutls_pkcs12_bag_deinit(gnutls_pkcs12_bag_t bag); + +int _pkcs12_string_to_key(unsigned int id, + const opaque * salt, + unsigned int salt_size, + unsigned int iter, + const char *pw, + unsigned int req_keylen, + opaque * keybuf); + +int _gnutls_pkcs7_decrypt_data(const gnutls_datum_t * data, + const char *password, + gnutls_datum_t * dec); + +typedef enum schema_id + { + PBES2, /* the stuff in PKCS #5 */ + PKCS12_3DES_SHA1, /* the fucking stuff in PKCS #12 */ + PKCS12_ARCFOUR_SHA1, + PKCS12_RC2_40_SHA1 + } schema_id; + +int _gnutls_pkcs7_encrypt_data(schema_id schema, + const gnutls_datum_t * data, + const char *password, + gnutls_datum_t * enc); +int _pkcs12_decode_safe_contents(const gnutls_datum_t * content, + gnutls_pkcs12_bag_t bag); + +int _pkcs12_encode_safe_contents(gnutls_pkcs12_bag_t bag, + ASN1_TYPE * content, + int *enc); + +int _pkcs12_decode_crt_bag(gnutls_pkcs12_bag_type_t type, + const gnutls_datum_t * in, + gnutls_datum_t * out); +int _pkcs12_encode_crt_bag(gnutls_pkcs12_bag_type_t type, + const gnutls_datum_t * raw, + gnutls_datum_t * out); + +#endif /* GNUTLS_PKCS12_H */ diff --git a/src/daemon/https/x509/pkcs12_bag.c b/src/daemon/https/x509/pkcs12_bag.c new file mode 100644 index 00000000..c34ec757 --- /dev/null +++ b/src/daemon/https/x509/pkcs12_bag.c @@ -0,0 +1,770 @@ +/* + * Copyright (C) 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +/* Functions that relate on PKCS12 Bag packet parsing. + */ + +#include + +#ifdef ENABLE_PKI + +#include +#include +#include +#include +#include +#include + +/** + * gnutls_pkcs12_bag_init - This function initializes a gnutls_pkcs12_bag_t structure + * @bag: The structure to be initialized + * + * This function will initialize a PKCS12 bag structure. PKCS12 Bags + * usually contain private keys, lists of X.509 Certificates and X.509 Certificate + * revocation lists. + * + * Returns 0 on success. + * + **/ +int +gnutls_pkcs12_bag_init (gnutls_pkcs12_bag_t * bag) +{ + *bag = gnutls_calloc (1, sizeof (gnutls_pkcs12_bag_int)); + + if (*bag) + { + return 0; /* success */ + } + return GNUTLS_E_MEMORY_ERROR; +} + +static inline void +_pkcs12_bag_free_data (gnutls_pkcs12_bag_t bag) +{ + int i; + + for (i = 0; i < bag->bag_elements; i++) + { + _gnutls_free_datum (&bag->element[i].data); + _gnutls_free_datum (&bag->element[i].local_key_id); + gnutls_free (bag->element[i].friendly_name); + bag->element[i].friendly_name = NULL; + bag->element[i].type = 0; + } + +} + + +/** + * gnutls_pkcs12_bag_deinit - This function deinitializes memory used by a gnutls_pkcs12_t structure + * @bag: The structure to be initialized + * + * This function will deinitialize a PKCS12 Bag structure. + * + **/ +void +gnutls_pkcs12_bag_deinit (gnutls_pkcs12_bag_t bag) +{ + if (!bag) + return; + + _pkcs12_bag_free_data (bag); + + gnutls_free (bag); +} + +/** + * gnutls_pkcs12_bag_get_type - This function returns the bag's type + * @bag: The bag + * @indx: The element of the bag to get the type + * + * This function will return the bag's type. One of the gnutls_pkcs12_bag_type_t + * enumerations. + * + **/ +gnutls_pkcs12_bag_type_t +gnutls_pkcs12_bag_get_type (gnutls_pkcs12_bag_t bag, int indx) +{ + if (bag == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + if (indx >= bag->bag_elements) + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + return bag->element[indx].type; +} + +/** + * gnutls_pkcs12_bag_get_count - This function returns the bag's elements count + * @bag: The bag + * + * This function will return the number of the elements withing the bag. + * + **/ +int +gnutls_pkcs12_bag_get_count (gnutls_pkcs12_bag_t bag) +{ + if (bag == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + return bag->bag_elements; +} + +/** + * gnutls_pkcs12_bag_get_data - This function returns the bag's data + * @bag: The bag + * @indx: The element of the bag to get the data from + * @data: where the bag's data will be. Should be treated as constant. + * + * This function will return the bag's data. The data is a constant + * that is stored into the bag. Should not be accessed after the bag + * is deleted. + * + * Returns 0 on success and a negative error code on error. + * + **/ +int +gnutls_pkcs12_bag_get_data (gnutls_pkcs12_bag_t bag, int indx, + gnutls_datum_t * data) +{ + if (bag == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + if (indx >= bag->bag_elements) + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + + data->data = bag->element[indx].data.data; + data->size = bag->element[indx].data.size; + + return 0; +} + +#define X509_CERT_OID "1.2.840.113549.1.9.22.1" +#define X509_CRL_OID "1.2.840.113549.1.9.23.1" + +int +_pkcs12_decode_crt_bag (gnutls_pkcs12_bag_type_t type, + const gnutls_datum_t * in, gnutls_datum_t * out) +{ + int ret; + ASN1_TYPE c2 = ASN1_TYPE_EMPTY; + + if (type == GNUTLS_BAG_CERTIFICATE) + { + if ((ret = asn1_create_element (_gnutls_get_pkix (), + "PKIX1.pkcs-12-CertBag", + &c2)) != ASN1_SUCCESS) + { + gnutls_assert (); + ret = _gnutls_asn2err (ret); + goto cleanup; + } + + ret = asn1_der_decoding (&c2, in->data, in->size, NULL); + if (ret != ASN1_SUCCESS) + { + gnutls_assert (); + ret = _gnutls_asn2err (ret); + goto cleanup; + } + + ret = _gnutls_x509_read_value (c2, "certValue", out, 1); + if (ret < 0) + { + gnutls_assert (); + goto cleanup; + } + + } + else + { /* CRL */ + if ((ret = asn1_create_element (_gnutls_get_pkix (), + "PKIX1.pkcs-12-CRLBag", + &c2)) != ASN1_SUCCESS) + { + gnutls_assert (); + ret = _gnutls_asn2err (ret); + goto cleanup; + } + + ret = asn1_der_decoding (&c2, in->data, in->size, NULL); + if (ret != ASN1_SUCCESS) + { + gnutls_assert (); + ret = _gnutls_asn2err (ret); + goto cleanup; + } + + ret = _gnutls_x509_read_value (c2, "crlValue", out, 1); + if (ret < 0) + { + gnutls_assert (); + goto cleanup; + } + } + + asn1_delete_structure (&c2); + + return 0; + + +cleanup: + + asn1_delete_structure (&c2); + return ret; +} + + +int +_pkcs12_encode_crt_bag (gnutls_pkcs12_bag_type_t type, + const gnutls_datum_t * raw, gnutls_datum_t * out) +{ + int ret; + ASN1_TYPE c2 = ASN1_TYPE_EMPTY; + + if (type == GNUTLS_BAG_CERTIFICATE) + { + if ((ret = asn1_create_element (_gnutls_get_pkix (), + "PKIX1.pkcs-12-CertBag", + &c2)) != ASN1_SUCCESS) + { + gnutls_assert (); + ret = _gnutls_asn2err (ret); + goto cleanup; + } + + ret = asn1_write_value (c2, "certId", X509_CERT_OID, 1); + if (ret != ASN1_SUCCESS) + { + gnutls_assert (); + ret = _gnutls_asn2err (ret); + goto cleanup; + } + + ret = _gnutls_x509_write_value (c2, "certValue", raw, 1); + if (ret < 0) + { + gnutls_assert (); + goto cleanup; + } + + } + else + { /* CRL */ + if ((ret = asn1_create_element (_gnutls_get_pkix (), + "PKIX1.pkcs-12-CRLBag", + &c2)) != ASN1_SUCCESS) + { + gnutls_assert (); + ret = _gnutls_asn2err (ret); + goto cleanup; + } + + ret = asn1_write_value (c2, "crlId", X509_CRL_OID, 1); + if (ret != ASN1_SUCCESS) + { + gnutls_assert (); + ret = _gnutls_asn2err (ret); + goto cleanup; + } + + ret = _gnutls_x509_write_value (c2, "crlValue", raw, 1); + if (ret < 0) + { + gnutls_assert (); + goto cleanup; + } + } + + ret = _gnutls_x509_der_encode (c2, "", out, 0); + + if (ret < 0) + { + gnutls_assert (); + goto cleanup; + } + + asn1_delete_structure (&c2); + + return 0; + + +cleanup: + + asn1_delete_structure (&c2); + return ret; +} + + +/** + * gnutls_pkcs12_bag_set_data - This function inserts data into the bag + * @bag: The bag + * @type: The data's type + * @data: the data to be copied. + * + * This function will insert the given data of the given type into the + * bag. + * + * Returns the index of the added bag on success, or a negative + * value on error. + * + **/ +int +gnutls_pkcs12_bag_set_data (gnutls_pkcs12_bag_t bag, + gnutls_pkcs12_bag_type_t type, + const gnutls_datum_t * data) +{ + int ret; + if (bag == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + if (bag->bag_elements == MAX_BAG_ELEMENTS - 1) + { + gnutls_assert (); + /* bag is full */ + return GNUTLS_E_MEMORY_ERROR; + } + + if (bag->bag_elements == 1) + { + /* A bag with a key or an encrypted bag, must have + * only one element. + */ + + if (bag->element[0].type == GNUTLS_BAG_PKCS8_KEY || + bag->element[0].type == GNUTLS_BAG_PKCS8_ENCRYPTED_KEY || + bag->element[0].type == GNUTLS_BAG_ENCRYPTED) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + } + + ret = + _gnutls_set_datum (&bag->element[bag->bag_elements].data, + data->data, data->size); + + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + bag->element[bag->bag_elements].type = type; + + bag->bag_elements++; + + return bag->bag_elements - 1; +} + +/** + * gnutls_pkcs12_bag_set_crt - This function inserts a certificate into the bag + * @bag: The bag + * @crt: the certificate to be copied. + * + * This function will insert the given certificate into the + * bag. This is just a wrapper over gnutls_pkcs12_bag_set_data(). + * + * Returns the index of the added bag on success, or a negative + * value on failure. + * + **/ +int +gnutls_pkcs12_bag_set_crt (gnutls_pkcs12_bag_t bag, gnutls_x509_crt_t crt) +{ + int ret; + gnutls_datum_t data; + + if (bag == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = _gnutls_x509_der_encode (crt->cert, "", &data, 0); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + ret = gnutls_pkcs12_bag_set_data (bag, GNUTLS_BAG_CERTIFICATE, &data); + + _gnutls_free_datum (&data); + + return ret; +} + +/** + * gnutls_pkcs12_bag_set_crl - This function inserts the CRL into the bag + * @bag: The bag + * @crl: the CRL to be copied. + * + * This function will insert the given CRL into the + * bag. This is just a wrapper over gnutls_pkcs12_bag_set_data(). + * + * Returns the index of the added bag on success, or a negative + * value on failure. + * + **/ +int +gnutls_pkcs12_bag_set_crl (gnutls_pkcs12_bag_t bag, gnutls_x509_crl_t crl) +{ + int ret; + gnutls_datum_t data; + + + if (bag == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = _gnutls_x509_der_encode (crl->crl, "", &data, 0); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + ret = gnutls_pkcs12_bag_set_data (bag, GNUTLS_BAG_CRL, &data); + + _gnutls_free_datum (&data); + + return ret; +} + +/** + * gnutls_pkcs12_bag_set_key_id - This function sets a key ID into the bag element + * @bag: The bag + * @indx: The bag's element to add the id + * @id: the ID + * + * This function will add the given key ID, to the specified, by the index, bag + * element. The key ID will be encoded as a 'Local key identifier' bag attribute, + * which is usually used to distinguish the local private key and the certificate pair. + * + * Returns 0 on success, or a negative value on error. + * + **/ +int +gnutls_pkcs12_bag_set_key_id (gnutls_pkcs12_bag_t bag, int indx, + const gnutls_datum_t * id) +{ + int ret; + + + if (bag == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + if (indx > bag->bag_elements - 1) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = _gnutls_set_datum (&bag->element[indx].local_key_id, + id->data, id->size); + + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + return 0; +} + +/** + * gnutls_pkcs12_bag_get_key_id - This function gets the key ID from the bag element + * @bag: The bag + * @indx: The bag's element to add the id + * @id: where the ID will be copied (to be treated as const) + * + * This function will return the key ID, of the specified bag element. + * The key ID is usually used to distinguish the local private key and the certificate pair. + * + * Returns 0 on success, or a negative value on error. + * + **/ +int +gnutls_pkcs12_bag_get_key_id (gnutls_pkcs12_bag_t bag, int indx, + gnutls_datum_t * id) +{ + if (bag == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + if (indx > bag->bag_elements - 1) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + id->data = bag->element[indx].local_key_id.data; + id->size = bag->element[indx].local_key_id.size; + + return 0; +} + +/** + * gnutls_pkcs12_bag_get_friendly_name - This function returns the friendly name of the bag element + * @bag: The bag + * @indx: The bag's element to add the id + * @name: will hold a pointer to the name (to be treated as const) + * + * This function will return the friendly name, of the specified bag element. + * The key ID is usually used to distinguish the local private key and the certificate pair. + * + * Returns 0 on success, or a negative value on error. + * + **/ +int +gnutls_pkcs12_bag_get_friendly_name (gnutls_pkcs12_bag_t bag, int indx, + char **name) +{ + if (bag == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + if (indx > bag->bag_elements - 1) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + *name = bag->element[indx].friendly_name; + + return 0; +} + + +/** + * gnutls_pkcs12_bag_set_friendly_name - This function sets a friendly name into the bag element + * @bag: The bag + * @indx: The bag's element to add the id + * @name: the name + * + * This function will add the given key friendly name, to the specified, by the index, bag + * element. The name will be encoded as a 'Friendly name' bag attribute, + * which is usually used to set a user name to the local private key and the certificate pair. + * + * Returns 0 on success, or a negative value on error. + * + **/ +int +gnutls_pkcs12_bag_set_friendly_name (gnutls_pkcs12_bag_t bag, int indx, + const char *name) +{ + if (bag == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + if (indx > bag->bag_elements - 1) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + bag->element[indx].friendly_name = gnutls_strdup (name); + + if (name == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + return 0; +} + + +/** + * gnutls_pkcs12_bag_decrypt - This function will decrypt an encrypted bag + * @bag: The bag + * @pass: The password used for encryption. This can only be ASCII. + * + * This function will decrypt the given encrypted bag and return 0 on success. + * + **/ +int +gnutls_pkcs12_bag_decrypt (gnutls_pkcs12_bag_t bag, const char *pass) +{ + int ret; + gnutls_datum_t dec; + + if (bag == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + if (bag->element[0].type != GNUTLS_BAG_ENCRYPTED) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = _gnutls_pkcs7_decrypt_data (&bag->element[0].data, pass, &dec); + + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + /* decryption succeeded. Now decode the SafeContents + * stuff, and parse it. + */ + + _gnutls_free_datum (&bag->element[0].data); + + ret = _pkcs12_decode_safe_contents (&dec, bag); + + _gnutls_free_datum (&dec); + + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + return 0; +} + +/** + * gnutls_pkcs12_bag_encrypt - This function will encrypt a bag + * @bag: The bag + * @pass: The password used for encryption. This can only be ASCII. + * @flags: should be one of gnutls_pkcs_encrypt_flags_t elements bitwise or'd + * + * This function will encrypt the given bag and return 0 on success. + * + **/ +int +gnutls_pkcs12_bag_encrypt (gnutls_pkcs12_bag_t bag, const char *pass, + unsigned int flags) +{ + int ret; + ASN1_TYPE safe_cont = ASN1_TYPE_EMPTY; + gnutls_datum_t der = { NULL, 0 }; + gnutls_datum_t enc = { NULL, 0 }; + schema_id id; + + if (bag == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + if (bag->element[0].type == GNUTLS_BAG_ENCRYPTED) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + /* Encode the whole bag to a safe contents + * structure. + */ + ret = _pkcs12_encode_safe_contents (bag, &safe_cont, NULL); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + /* DER encode the SafeContents. + */ + ret = _gnutls_x509_der_encode (safe_cont, "", &der, 0); + + asn1_delete_structure (&safe_cont); + + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + if (flags & GNUTLS_PKCS_PLAIN) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + if (flags & GNUTLS_PKCS_USE_PKCS12_ARCFOUR) + id = PKCS12_ARCFOUR_SHA1; + else if (flags & GNUTLS_PKCS_USE_PKCS12_RC2_40) + id = PKCS12_RC2_40_SHA1; + else if (flags & GNUTLS_PKCS_USE_PBES2_3DES) + id = PBES2; + else + id = PKCS12_3DES_SHA1; + + /* Now encrypt them. + */ + ret = _gnutls_pkcs7_encrypt_data (id, &der, pass, &enc); + + _gnutls_free_datum (&der); + + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + /* encryption succeeded. + */ + + _pkcs12_bag_free_data (bag); + + bag->element[0].type = GNUTLS_BAG_ENCRYPTED; + bag->element[0].data = enc; + + bag->bag_elements = 1; + + + return 0; +} + + +#endif /* ENABLE_PKI */ diff --git a/src/daemon/https/x509/pkcs12_encr.c b/src/daemon/https/x509/pkcs12_encr.c new file mode 100644 index 00000000..c80d23a3 --- /dev/null +++ b/src/daemon/https/x509/pkcs12_encr.c @@ -0,0 +1,169 @@ +/* minip12.c - A mini pkcs-12 implementation (modified for gnutls) + * + * Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +#include + +#ifdef ENABLE_PKI + +#include +#include +#include + +/* Returns 0 if the password is ok, or a negative error + * code instead. + */ +static int +_pkcs12_check_pass (const char *pass, size_t plen) +{ + const unsigned char *p = pass; + unsigned int i; + + for (i = 0; i < plen; i++) + { + if (isascii (p[i])) + continue; + return GNUTLS_E_INVALID_PASSWORD; + } + + return 0; +} + +/* ID should be: + * 3 for MAC + * 2 for IV + * 1 for encryption key + */ +int +_pkcs12_string_to_key (unsigned int id, const opaque * salt, + unsigned int salt_size, unsigned int iter, + const char *pw, unsigned int req_keylen, + opaque * keybuf) +{ + int rc; + unsigned int i, j; + gc_hash_handle md; + mpi_t num_b1 = NULL; + unsigned int pwlen; + opaque hash[20], buf_b[64], buf_i[128], *p; + size_t cur_keylen; + size_t n; + + cur_keylen = 0; + + if (pw == NULL) + pwlen = 0; + else + pwlen = strlen (pw); + + if (pwlen > 63 / 2) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + if ((rc = _pkcs12_check_pass (pw, pwlen)) < 0) + { + gnutls_assert (); + return rc; + } + + /* Store salt and password in BUF_I */ + p = buf_i; + for (i = 0; i < 64; i++) + *p++ = salt[i % salt_size]; + if (pw) + { + for (i = j = 0; i < 64; i += 2) + { + *p++ = 0; + *p++ = pw[j]; + if (++j > pwlen) /* Note, that we include the trailing zero */ + j = 0; + } + } + else + memset (p, 0, 64); + + for (;;) + { + rc = gc_hash_open (GC_SHA1, 0, &md); + if (rc) + { + gnutls_assert (); + return GNUTLS_E_DECRYPTION_FAILED; + } + for (i = 0; i < 64; i++) + { + unsigned char lid = id & 0xFF; + gc_hash_write (md, 1, &lid); + } + gc_hash_write (md, pw ? 128 : 64, buf_i); + memcpy (hash, gc_hash_read (md), 20); + gc_hash_close (md); + for (i = 1; i < iter; i++) + gc_hash_buffer (GC_SHA1, hash, 20, hash); + for (i = 0; i < 20 && cur_keylen < req_keylen; i++) + keybuf[cur_keylen++] = hash[i]; + if (cur_keylen == req_keylen) + { + gcry_mpi_release (num_b1); + return 0; /* ready */ + } + + /* need more bytes. */ + for (i = 0; i < 64; i++) + buf_b[i] = hash[i % 20]; + n = 64; + rc = _gnutls_mpi_scan (&num_b1, buf_b, &n); + if (rc < 0) + { + gnutls_assert (); + return rc; + } + gcry_mpi_add_ui (num_b1, num_b1, 1); + for (i = 0; i < 128; i += 64) + { + mpi_t num_ij; + + n = 64; + rc = _gnutls_mpi_scan (&num_ij, buf_i + i, &n); + if (rc < 0) + { + gnutls_assert (); + return rc; + } + gcry_mpi_add (num_ij, num_ij, num_b1); + gcry_mpi_clear_highbit (num_ij, 64 * 8); + n = 64; + rc = _gnutls_mpi_print (buf_i + i, &n, num_ij); + if (rc < 0) + { + gnutls_assert (); + return rc; + } + gcry_mpi_release (num_ij); + } + } +} + +#endif /* ENABLE_PKI */ diff --git a/src/daemon/https/x509/pkcs7.c b/src/daemon/https/x509/pkcs7.c new file mode 100644 index 00000000..35f21b12 --- /dev/null +++ b/src/daemon/https/x509/pkcs7.c @@ -0,0 +1,1023 @@ +/* + * Copyright (C) 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +/* Functions that relate on PKCS7 certificate lists parsing. + */ + +#include +#include + +#ifdef ENABLE_PKI + +#include +#include +#include +#include +#include +#include +#include + +#define SIGNED_DATA_OID "1.2.840.113549.1.7.2" + +/* Decodes the PKCS #7 signed data, and returns an ASN1_TYPE, + * which holds them. If raw is non null then the raw decoded + * data are copied (they are locally allocated) there. + */ +static int +_decode_pkcs7_signed_data (ASN1_TYPE pkcs7, ASN1_TYPE * sdata, + gnutls_datum_t * raw) +{ + char oid[128]; + ASN1_TYPE c2; + opaque *tmp = NULL; + int tmp_size, len, result; + + len = sizeof (oid) - 1; + result = asn1_read_value (pkcs7, "contentType", oid, &len); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + if (strcmp (oid, SIGNED_DATA_OID) != 0) + { + gnutls_assert (); + _gnutls_x509_log ("Unknown PKCS7 Content OID '%s'\n", oid); + return GNUTLS_E_UNKNOWN_PKCS_CONTENT_TYPE; + } + + if ((result = asn1_create_element + (_gnutls_get_pkix (), "PKIX1.pkcs-7-SignedData", &c2)) != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + /* the Signed-data has been created, so + * decode them. + */ + tmp_size = 0; + result = asn1_read_value (pkcs7, "content", NULL, &tmp_size); + if (result != ASN1_MEM_ERROR) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + tmp = gnutls_malloc (tmp_size); + if (tmp == NULL) + { + gnutls_assert (); + result = GNUTLS_E_MEMORY_ERROR; + goto cleanup; + } + + result = asn1_read_value (pkcs7, "content", tmp, &tmp_size); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + /* tmp, tmp_size hold the data and the size of the CertificateSet structure + * actually the ANY stuff. + */ + + /* Step 1. In case of a signed structure extract certificate set. + */ + + result = asn1_der_decoding (&c2, tmp, tmp_size, NULL); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + if (raw == NULL) + { + gnutls_free (tmp); + } + else + { + raw->data = tmp; + raw->size = tmp_size; + } + + *sdata = c2; + + return 0; + +cleanup: + if (c2) + asn1_delete_structure (&c2); + gnutls_free (tmp); + return result; +} + +/** + * gnutls_pkcs7_init - This function initializes a gnutls_pkcs7_t structure + * @pkcs7: The structure to be initialized + * + * This function will initialize a PKCS7 structure. PKCS7 structures + * usually contain lists of X.509 Certificates and X.509 Certificate + * revocation lists. + * + * Returns 0 on success. + * + **/ +int +gnutls_pkcs7_init (gnutls_pkcs7_t * pkcs7) +{ + *pkcs7 = gnutls_calloc (1, sizeof (gnutls_pkcs7_int)); + + if (*pkcs7) + { + int result = asn1_create_element (_gnutls_get_pkix (), + "PKIX1.pkcs-7-ContentInfo", + &(*pkcs7)->pkcs7); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + gnutls_free (*pkcs7); + return _gnutls_asn2err (result); + } + return 0; /* success */ + } + return GNUTLS_E_MEMORY_ERROR; +} + +/** + * gnutls_pkcs7_deinit - This function deinitializes memory used by a gnutls_pkcs7_t structure + * @pkcs7: The structure to be initialized + * + * This function will deinitialize a PKCS7 structure. + * + **/ +void +gnutls_pkcs7_deinit (gnutls_pkcs7_t pkcs7) +{ + if (!pkcs7) + return; + + if (pkcs7->pkcs7) + asn1_delete_structure (&pkcs7->pkcs7); + + gnutls_free (pkcs7); +} + +/** + * gnutls_pkcs7_import - This function will import a DER or PEM encoded PKCS7 + * @pkcs7: The structure to store the parsed PKCS7. + * @data: The DER or PEM encoded PKCS7. + * @format: One of DER or PEM + * + * This function will convert the given DER or PEM encoded PKCS7 + * to the native gnutls_pkcs7_t format. The output will be stored in 'pkcs7'. + * + * If the PKCS7 is PEM encoded it should have a header of "PKCS7". + * + * Returns 0 on success. + * + **/ +int +gnutls_pkcs7_import (gnutls_pkcs7_t pkcs7, const gnutls_datum_t * data, + gnutls_x509_crt_fmt_t format) +{ + int result = 0, need_free = 0; + gnutls_datum_t _data; + + if (pkcs7 == NULL) + return GNUTLS_E_INVALID_REQUEST; + + _data.data = data->data; + _data.size = data->size; + + /* If the PKCS7 is in PEM format then decode it + */ + if (format == GNUTLS_X509_FMT_PEM) + { + opaque *out; + + result = _gnutls_fbase64_decode (PEM_PKCS7, data->data, data->size, + &out); + + if (result <= 0) + { + if (result == 0) + result = GNUTLS_E_INTERNAL_ERROR; + gnutls_assert (); + return result; + } + + _data.data = out; + _data.size = result; + + need_free = 1; + } + + + result = asn1_der_decoding (&pkcs7->pkcs7, _data.data, _data.size, NULL); + if (result != ASN1_SUCCESS) + { + result = _gnutls_asn2err (result); + gnutls_assert (); + goto cleanup; + } + + if (need_free) + _gnutls_free_datum (&_data); + + return 0; + +cleanup: + if (need_free) + _gnutls_free_datum (&_data); + return result; +} + +/** + * gnutls_pkcs7_get_crt_raw - This function returns a certificate in a PKCS7 certificate set + * @pkcs7_struct: should contain a gnutls_pkcs7_t structure + * @indx: contains the index of the certificate to extract + * @certificate: the contents of the certificate will be copied there (may be null) + * @certificate_size: should hold the size of the certificate + * + * This function will return a certificate of the PKCS7 or RFC2630 certificate set. + * Returns 0 on success. If the provided buffer is not long enough, + * then @certificate_size is updated and GNUTLS_E_SHORT_MEMORY_BUFFER is returned. + * + * After the last certificate has been read GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE + * will be returned. + * + **/ +int +gnutls_pkcs7_get_crt_raw (gnutls_pkcs7_t pkcs7, + int indx, void *certificate, + size_t * certificate_size) +{ + ASN1_TYPE c2 = ASN1_TYPE_EMPTY; + int result, len; + char root2[MAX_NAME_SIZE]; + char oid[128]; + gnutls_datum_t tmp = { NULL, 0 }; + + if (certificate_size == NULL || pkcs7 == NULL) + return GNUTLS_E_INVALID_REQUEST; + + /* Step 1. decode the signed data. + */ + result = _decode_pkcs7_signed_data (pkcs7->pkcs7, &c2, &tmp); + if (result < 0) + { + gnutls_assert (); + return result; + } + + /* Step 2. Parse the CertificateSet + */ + + snprintf (root2, sizeof (root2), "certificates.?%u", indx + 1); + + len = sizeof (oid) - 1; + + result = asn1_read_value (c2, root2, oid, &len); + + if (result == ASN1_VALUE_NOT_FOUND) + { + result = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + goto cleanup; + } + + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + /* if 'Certificate' is the choice found: + */ + if (strcmp (oid, "certificate") == 0) + { + int start, end; + + result = asn1_der_decoding_startEnd (c2, tmp.data, tmp.size, + root2, &start, &end); + + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + end = end - start + 1; + + if ((unsigned) end > *certificate_size) + { + *certificate_size = end; + result = GNUTLS_E_SHORT_MEMORY_BUFFER; + goto cleanup; + } + + if (certificate) + memcpy (certificate, &tmp.data[start], end); + + *certificate_size = end; + + result = 0; + + } + else + { + result = GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE; + } + +cleanup: + _gnutls_free_datum (&tmp); + if (c2) + asn1_delete_structure (&c2); + return result; +} + +/** + * gnutls_pkcs7_get_crt_count - This function returns the number of certificates in a PKCS7 certificate set + * @pkcs7_struct: should contain a gnutls_pkcs7_t structure + * + * This function will return the number of certifcates in the PKCS7 or + * RFC2630 certificate set. + * + * Returns a negative value on failure. + * + **/ +int +gnutls_pkcs7_get_crt_count (gnutls_pkcs7_t pkcs7) +{ + ASN1_TYPE c2 = ASN1_TYPE_EMPTY; + int result, count; + + if (pkcs7 == NULL) + return GNUTLS_E_INVALID_REQUEST; + + /* Step 1. decode the signed data. + */ + result = _decode_pkcs7_signed_data (pkcs7->pkcs7, &c2, NULL); + if (result < 0) + { + gnutls_assert (); + return result; + } + + /* Step 2. Count the CertificateSet */ + + result = asn1_number_of_elements (c2, "certificates", &count); + + asn1_delete_structure (&c2); + + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return 0; /* no certificates */ + } + + return count; + +} + +/** + * gnutls_pkcs7_export - This function will export the pkcs7 structure + * @pkcs7: Holds the pkcs7 structure + * @format: the format of output params. One of PEM or DER. + * @output_data: will contain a structure PEM or DER encoded + * @output_data_size: holds the size of output_data (and will be + * replaced by the actual size of parameters) + * + * This function will export the pkcs7 structure to DER or PEM format. + * + * If the buffer provided is not long enough to hold the output, then + * *output_data_size is updated and GNUTLS_E_SHORT_MEMORY_BUFFER will + * be returned. + * + * If the structure is PEM encoded, it will have a header + * of "BEGIN PKCS7". + * + * Return value: In case of failure a negative value will be + * returned, and 0 on success. + * + **/ +int +gnutls_pkcs7_export (gnutls_pkcs7_t pkcs7, + gnutls_x509_crt_fmt_t format, void *output_data, + size_t * output_data_size) +{ + if (pkcs7 == NULL) + return GNUTLS_E_INVALID_REQUEST; + + return _gnutls_x509_export_int (pkcs7->pkcs7, format, PEM_PKCS7, + output_data, output_data_size); +} + +/* Creates an empty signed data structure in the pkcs7 + * structure and returns a handle to the signed data. + */ +static int +create_empty_signed_data (ASN1_TYPE pkcs7, ASN1_TYPE * sdata) +{ + uint8_t one = 1; + int result; + + *sdata = ASN1_TYPE_EMPTY; + + if ((result = asn1_create_element + (_gnutls_get_pkix (), "PKIX1.pkcs-7-SignedData", + sdata)) != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + /* Use version 1 + */ + result = asn1_write_value (*sdata, "version", &one, 1); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + /* Use no digest algorithms + */ + + /* id-data */ + result = + asn1_write_value (*sdata, "encapContentInfo.eContentType", + "1.2.840.113549.1.7.5", 1); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + result = asn1_write_value (*sdata, "encapContentInfo.eContent", NULL, 0); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + /* Add no certificates. + */ + + /* Add no crls. + */ + + /* Add no signerInfos. + */ + + /* Write the content type of the signed data + */ + result = asn1_write_value (pkcs7, "contentType", SIGNED_DATA_OID, 1); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + return 0; + +cleanup: + asn1_delete_structure (sdata); + return result; + +} + +/** + * gnutls_pkcs7_set_crt_raw - This function adds a certificate in a PKCS7 certificate set + * @pkcs7_struct: should contain a gnutls_pkcs7_t structure + * @crt: the DER encoded certificate to be added + * + * This function will add a certificate to the PKCS7 or RFC2630 certificate set. + * Returns 0 on success. + * + **/ +int +gnutls_pkcs7_set_crt_raw (gnutls_pkcs7_t pkcs7, const gnutls_datum_t * crt) +{ + ASN1_TYPE c2 = ASN1_TYPE_EMPTY; + int result; + + if (pkcs7 == NULL) + return GNUTLS_E_INVALID_REQUEST; + + /* Step 1. decode the signed data. + */ + result = _decode_pkcs7_signed_data (pkcs7->pkcs7, &c2, NULL); + if (result < 0 && result != GNUTLS_E_ASN1_VALUE_NOT_FOUND) + { + gnutls_assert (); + return result; + } + + /* If the signed data are uninitialized + * then create them. + */ + if (result == GNUTLS_E_ASN1_VALUE_NOT_FOUND) + { + /* The pkcs7 structure is new, so create the + * signedData. + */ + result = create_empty_signed_data (pkcs7->pkcs7, &c2); + if (result < 0) + { + gnutls_assert (); + return result; + } + } + + /* Step 2. Append the new certificate. + */ + + result = asn1_write_value (c2, "certificates", "NEW", 1); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + result = asn1_write_value (c2, "certificates.?LAST", "certificate", 1); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + result = + asn1_write_value (c2, "certificates.?LAST.certificate", crt->data, + crt->size); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + /* Step 3. Replace the old content with the new + */ + result = + _gnutls_x509_der_encode_and_copy (c2, "", pkcs7->pkcs7, "content", 0); + if (result < 0) + { + gnutls_assert (); + goto cleanup; + } + + asn1_delete_structure (&c2); + + return 0; + +cleanup: + if (c2) + asn1_delete_structure (&c2); + return result; +} + +/** + * gnutls_pkcs7_set_crt - This function adds a parsed certificate in a PKCS7 certificate set + * @pkcs7_struct: should contain a gnutls_pkcs7_t structure + * @crt: the certificate to be copied. + * + * This function will add a parsed certificate to the PKCS7 or RFC2630 certificate set. + * This is a wrapper function over gnutls_pkcs7_set_crt_raw() . + * + * Returns 0 on success. + * + **/ +int +gnutls_pkcs7_set_crt (gnutls_pkcs7_t pkcs7, gnutls_x509_crt_t crt) +{ + int ret; + gnutls_datum_t data; + + if (pkcs7 == NULL) + return GNUTLS_E_INVALID_REQUEST; + + ret = _gnutls_x509_der_encode (crt->cert, "", &data, 0); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + ret = gnutls_pkcs7_set_crt_raw (pkcs7, &data); + + _gnutls_free_datum (&data); + + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + return 0; +} + + +/** + * gnutls_pkcs7_delete_crt - This function deletes a certificate from a PKCS7 certificate set + * @pkcs7_struct: should contain a gnutls_pkcs7_t structure + * @indx: the index of the certificate to delete + * + * This function will delete a certificate from a PKCS7 or RFC2630 certificate set. + * Index starts from 0. Returns 0 on success. + * + **/ +int +gnutls_pkcs7_delete_crt (gnutls_pkcs7_t pkcs7, int indx) +{ + ASN1_TYPE c2 = ASN1_TYPE_EMPTY; + int result; + char root2[MAX_NAME_SIZE]; + + if (pkcs7 == NULL) + return GNUTLS_E_INVALID_REQUEST; + + /* Step 1. Decode the signed data. + */ + result = _decode_pkcs7_signed_data (pkcs7->pkcs7, &c2, NULL); + if (result < 0) + { + gnutls_assert (); + return result; + } + + /* Step 2. Delete the certificate. + */ + + snprintf (root2, sizeof (root2), "certificates.?%u", indx + 1); + + result = asn1_write_value (c2, root2, NULL, 0); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + /* Step 3. Replace the old content with the new + */ + result = + _gnutls_x509_der_encode_and_copy (c2, "", pkcs7->pkcs7, "content", 0); + if (result < 0) + { + gnutls_assert (); + goto cleanup; + } + + asn1_delete_structure (&c2); + + return 0; + +cleanup: + if (c2) + asn1_delete_structure (&c2); + return result; +} + +/* Read and write CRLs + */ + +/** + * gnutls_pkcs7_get_crl_raw - This function returns a crl in a PKCS7 crl set + * @pkcs7_struct: should contain a gnutls_pkcs7_t structure + * @indx: contains the index of the crl to extract + * @crl: the contents of the crl will be copied there (may be null) + * @crl_size: should hold the size of the crl + * + * This function will return a crl of the PKCS7 or RFC2630 crl set. + * Returns 0 on success. If the provided buffer is not long enough, + * then @crl_size is updated and GNUTLS_E_SHORT_MEMORY_BUFFER is returned. + * + * After the last crl has been read GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE + * will be returned. + * + **/ +int +gnutls_pkcs7_get_crl_raw (gnutls_pkcs7_t pkcs7, + int indx, void *crl, size_t * crl_size) +{ + ASN1_TYPE c2 = ASN1_TYPE_EMPTY; + int result; + char root2[MAX_NAME_SIZE]; + gnutls_datum_t tmp = { NULL, 0 }; + int start, end; + + if (pkcs7 == NULL || crl_size == NULL) + return GNUTLS_E_INVALID_REQUEST; + + /* Step 1. decode the signed data. + */ + result = _decode_pkcs7_signed_data (pkcs7->pkcs7, &c2, &tmp); + if (result < 0) + { + gnutls_assert (); + return result; + } + + /* Step 2. Parse the CertificateSet + */ + + snprintf (root2, sizeof (root2), "crls.?%u", indx + 1); + + /* Get the raw CRL + */ + result = asn1_der_decoding_startEnd (c2, tmp.data, tmp.size, + root2, &start, &end); + + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + end = end - start + 1; + + if ((unsigned) end > *crl_size) + { + *crl_size = end; + result = GNUTLS_E_SHORT_MEMORY_BUFFER; + goto cleanup; + } + + if (crl) + memcpy (crl, &tmp.data[start], end); + + *crl_size = end; + + result = 0; + +cleanup: + _gnutls_free_datum (&tmp); + if (c2) + asn1_delete_structure (&c2); + return result; +} + +/** + * gnutls_pkcs7_get_crl_count - This function returns the number of crls in a PKCS7 crl set + * @pkcs7_struct: should contain a gnutls_pkcs7_t structure + * + * This function will return the number of certifcates in the PKCS7 or + * RFC2630 crl set. + * + * Returns a negative value on failure. + * + **/ +int +gnutls_pkcs7_get_crl_count (gnutls_pkcs7_t pkcs7) +{ + ASN1_TYPE c2 = ASN1_TYPE_EMPTY; + int result, count; + + if (pkcs7 == NULL) + return GNUTLS_E_INVALID_REQUEST; + + /* Step 1. decode the signed data. + */ + result = _decode_pkcs7_signed_data (pkcs7->pkcs7, &c2, NULL); + if (result < 0) + { + gnutls_assert (); + return result; + } + + /* Step 2. Count the CertificateSet */ + + result = asn1_number_of_elements (c2, "crls", &count); + + asn1_delete_structure (&c2); + + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return 0; /* no crls */ + } + + return count; + +} + +/** + * gnutls_pkcs7_set_crl_raw - This function adds a crl in a PKCS7 crl set + * @pkcs7_struct: should contain a gnutls_pkcs7_t structure + * @crl: the DER encoded crl to be added + * + * This function will add a crl to the PKCS7 or RFC2630 crl set. + * Returns 0 on success. + * + **/ +int +gnutls_pkcs7_set_crl_raw (gnutls_pkcs7_t pkcs7, const gnutls_datum_t * crl) +{ + ASN1_TYPE c2 = ASN1_TYPE_EMPTY; + int result; + + if (pkcs7 == NULL) + return GNUTLS_E_INVALID_REQUEST; + + /* Step 1. decode the signed data. + */ + result = _decode_pkcs7_signed_data (pkcs7->pkcs7, &c2, NULL); + if (result < 0 && result != GNUTLS_E_ASN1_VALUE_NOT_FOUND) + { + gnutls_assert (); + return result; + } + + /* If the signed data are uninitialized + * then create them. + */ + if (result == GNUTLS_E_ASN1_VALUE_NOT_FOUND) + { + /* The pkcs7 structure is new, so create the + * signedData. + */ + result = create_empty_signed_data (pkcs7->pkcs7, &c2); + if (result < 0) + { + gnutls_assert (); + return result; + } + } + + /* Step 2. Append the new crl. + */ + + result = asn1_write_value (c2, "crls", "NEW", 1); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + result = asn1_write_value (c2, "crls.?LAST", crl->data, crl->size); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + /* Step 3. Replace the old content with the new + */ + result = + _gnutls_x509_der_encode_and_copy (c2, "", pkcs7->pkcs7, "content", 0); + if (result < 0) + { + gnutls_assert (); + goto cleanup; + } + + asn1_delete_structure (&c2); + + return 0; + +cleanup: + if (c2) + asn1_delete_structure (&c2); + return result; +} + +/** + * gnutls_pkcs7_set_crl - This function adds a parsed crl in a PKCS7 crl set + * @pkcs7_struct: should contain a gnutls_pkcs7_t structure + * @crl: the DER encoded crl to be added + * + * This function will add a parsed crl to the PKCS7 or RFC2630 crl set. + * Returns 0 on success. + * + **/ +int +gnutls_pkcs7_set_crl (gnutls_pkcs7_t pkcs7, gnutls_x509_crl_t crl) +{ + int ret; + gnutls_datum_t data; + + if (pkcs7 == NULL) + return GNUTLS_E_INVALID_REQUEST; + + ret = _gnutls_x509_der_encode (crl->crl, "", &data, 0); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + ret = gnutls_pkcs7_set_crl_raw (pkcs7, &data); + + _gnutls_free_datum (&data); + + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + return 0; +} + +/** + * gnutls_pkcs7_delete_crl - This function deletes a crl from a PKCS7 crl set + * @pkcs7_struct: should contain a gnutls_pkcs7_t structure + * @indx: the index of the crl to delete + * + * This function will delete a crl from a PKCS7 or RFC2630 crl set. + * Index starts from 0. Returns 0 on success. + * + **/ +int +gnutls_pkcs7_delete_crl (gnutls_pkcs7_t pkcs7, int indx) +{ + ASN1_TYPE c2 = ASN1_TYPE_EMPTY; + int result; + char root2[MAX_NAME_SIZE]; + + if (pkcs7 == NULL) + return GNUTLS_E_INVALID_REQUEST; + + /* Step 1. Decode the signed data. + */ + result = _decode_pkcs7_signed_data (pkcs7->pkcs7, &c2, NULL); + if (result < 0) + { + gnutls_assert (); + return result; + } + + /* Step 2. Delete the crl. + */ + + snprintf (root2, sizeof (root2), "crls.?%u", indx + 1); + + result = asn1_write_value (c2, root2, NULL, 0); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + /* Step 3. Replace the old content with the new + */ + result = + _gnutls_x509_der_encode_and_copy (c2, "", pkcs7->pkcs7, "content", 0); + if (result < 0) + { + gnutls_assert (); + goto cleanup; + } + + asn1_delete_structure (&c2); + + return 0; + +cleanup: + if (c2) + asn1_delete_structure (&c2); + return result; +} + +#endif /* ENABLE_PKI */ diff --git a/src/daemon/https/x509/pkcs7.h b/src/daemon/https/x509/pkcs7.h new file mode 100644 index 00000000..ee1990c3 --- /dev/null +++ b/src/daemon/https/x509/pkcs7.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +#include "x509.h" + +typedef struct gnutls_pkcs7_int +{ + ASN1_TYPE pkcs7; +} gnutls_pkcs7_int; diff --git a/src/daemon/https/x509/privkey.h b/src/daemon/https/x509/privkey.h new file mode 100644 index 00000000..6e645b9d --- /dev/null +++ b/src/daemon/https/x509/privkey.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +#include "x509.h" + +ASN1_TYPE _gnutls_privkey_decode_pkcs1_rsa_key (const gnutls_datum_t * + raw_key, + gnutls_x509_privkey_t pkey); + +int _gnutls_asn1_encode_dsa (ASN1_TYPE * c2, mpi_t * params); diff --git a/src/daemon/https/x509/privkey_pkcs8.c b/src/daemon/https/x509/privkey_pkcs8.c new file mode 100644 index 00000000..2f1921bb --- /dev/null +++ b/src/daemon/https/x509/privkey_pkcs8.c @@ -0,0 +1,2219 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2007 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +#include + +#ifdef ENABLE_PKI + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "gc.h" + +#define PBES2_OID "1.2.840.113549.1.5.13" +#define PBKDF2_OID "1.2.840.113549.1.5.12" +#define DES_EDE3_CBC_OID "1.2.840.113549.3.7" +#define DES_CBC_OID "1.3.14.3.2.7" + +/* oid_pbeWithSHAAnd3_KeyTripleDES_CBC */ +#define PKCS12_PBE_3DES_SHA1_OID "1.2.840.113549.1.12.1.3" +#define PKCS12_PBE_ARCFOUR_SHA1_OID "1.2.840.113549.1.12.1.1" +#define PKCS12_PBE_RC2_40_SHA1_OID "1.2.840.113549.1.12.1.6" + +struct pbkdf2_params + { + opaque salt[32]; + int salt_size; + unsigned int iter_count; + unsigned int key_size; + }; + +struct pbe_enc_params + { + gnutls_cipher_algorithm_t cipher; + opaque iv[8]; + int iv_size; + }; + +static int generate_key (schema_id schema, const char *password, + struct pbkdf2_params *kdf_params, + struct pbe_enc_params *enc_params, + gnutls_datum_t * key); +static int read_pbkdf2_params (ASN1_TYPE pbes2_asn, + const gnutls_datum_t * der, + struct pbkdf2_params *params); +static int read_pbe_enc_params (ASN1_TYPE pbes2_asn, + const gnutls_datum_t * der, + struct pbe_enc_params *params); +static int decrypt_data (schema_id, ASN1_TYPE pkcs8_asn, const char *root, + const char *password, + const struct pbkdf2_params *kdf_params, + const struct pbe_enc_params *enc_params, + gnutls_datum_t * decrypted_data); +static int decode_private_key_info (const gnutls_datum_t * der, + gnutls_x509_privkey_t pkey); +static int write_schema_params (schema_id schema, ASN1_TYPE pkcs8_asn, + const char *where, + const struct pbkdf2_params *kdf_params, + const struct pbe_enc_params *enc_params); +static int encrypt_data (const gnutls_datum_t * plain, + const struct pbe_enc_params *enc_params, + gnutls_datum_t * key, gnutls_datum_t * encrypted); + +static int read_pkcs12_kdf_params (ASN1_TYPE pbes2_asn, + struct pbkdf2_params *params); +static int write_pkcs12_kdf_params (ASN1_TYPE pbes2_asn, + const struct pbkdf2_params *params); + +#define PEM_PKCS8 "ENCRYPTED PRIVATE KEY" +#define PEM_UNENCRYPTED_PKCS8 "PRIVATE KEY" + +/* Returns a negative error code if the encryption schema in + * the OID is not supported. The schema ID is returned. + */ +inline static int +check_schema (const char *oid) + { + + if (strcmp (oid, PBES2_OID) == 0) + return PBES2; + + if (strcmp (oid, PKCS12_PBE_3DES_SHA1_OID) == 0) + return PKCS12_3DES_SHA1; + + if (strcmp (oid, PKCS12_PBE_ARCFOUR_SHA1_OID) == 0) + return PKCS12_ARCFOUR_SHA1; + + if (strcmp (oid, PKCS12_PBE_RC2_40_SHA1_OID) == 0) + return PKCS12_RC2_40_SHA1; + + _gnutls_x509_log ("PKCS encryption schema OID '%s' is unsupported.\n", oid); + + return GNUTLS_E_UNKNOWN_CIPHER_TYPE; + } + +/* Encodes a private key to the raw format PKCS #8 needs. + * For RSA it is a PKCS #1 DER private key and for DSA it is + * an ASN.1 INTEGER of the x value. + */ +inline static int +_encode_privkey (gnutls_x509_privkey pkey, gnutls_datum * raw) + { + size_t size = 0; + opaque *data = NULL; + int ret; + ASN1_TYPE spk = ASN1_TYPE_EMPTY; + + switch (pkey->pk_algorithm) + { + case GNUTLS_PK_RSA: + ret = + gnutls_x509_privkey_export (pkey, GNUTLS_X509_FMT_DER, NULL, &size); + if (ret != GNUTLS_E_SHORT_MEMORY_BUFFER) + { + gnutls_assert (); + goto error; + } + + data = gnutls_malloc (size); + if (data == NULL) + { + gnutls_assert (); + ret = GNUTLS_E_MEMORY_ERROR; + goto error; + } + + ret = + gnutls_x509_privkey_export (pkey, GNUTLS_X509_FMT_DER, data, &size); + if (ret < 0) + { + gnutls_assert (); + goto error; + } + + raw->data = data; + raw->size = size; + break; + default: + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + return 0; + + error: + gnutls_free (data); + asn1_delete_structure (&spk); + return ret; + + } + +/* + * Encodes a PKCS #1 private key to a PKCS #8 private key + * info. The output will be allocated and stored into der. Also + * the ASN1_TYPE of private key info will be returned. + */ +static int +encode_to_private_key_info (gnutls_x509_privkey_t pkey, + gnutls_datum_t * der, ASN1_TYPE * pkey_info) + { + int result, len; + opaque null = 0; + const char *oid; + gnutls_datum algo_params = + { NULL, 0}; + gnutls_datum algo_privkey = + { NULL, 0}; + + if (pkey->pk_algorithm == GNUTLS_PK_RSA) + { + oid = PK_PKIX1_RSA_OID; + /* parameters are null + */ + } + else + { + oid = PK_DSA_OID; + result = + _gnutls_x509_write_dsa_params (pkey->params, pkey->params_size, + &algo_params); + if (result < 0) + { + gnutls_assert (); + return result; + } + } + + if ((result = + asn1_create_element (_gnutls_get_pkix (), + "PKIX1.pkcs-8-PrivateKeyInfo", + pkey_info)) != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto error; + } + + /* Write the version. + */ + result = asn1_write_value (*pkey_info, "version", &null, 1); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto error; + } + + /* write the privateKeyAlgorithm + * fields. (OID+NULL data) + */ + result = + asn1_write_value (*pkey_info, "privateKeyAlgorithm.algorithm", oid, 1); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto error; + } + + result = + asn1_write_value (*pkey_info, "privateKeyAlgorithm.parameters", + algo_params.data, algo_params.size); + _gnutls_free_datum (&algo_params); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto error; + } + + /* Write the raw private key + */ + result = _encode_privkey (pkey, &algo_privkey); + if (result < 0) + { + gnutls_assert (); + goto error; + } + + result = + asn1_write_value (*pkey_info, "privateKey", algo_privkey.data, + algo_privkey.size); + _gnutls_free_datum (&algo_privkey); + + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto error; + } + + /* Append an empty Attributes field. + */ + result = asn1_write_value (*pkey_info, "attributes", NULL, 0); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto error; + } + + /* DER Encode the generated private key info. + */ + len = 0; + result = asn1_der_coding (*pkey_info, "", NULL, &len, NULL); + if (result != ASN1_MEM_ERROR) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto error; + } + + /* allocate data for the der + */ + der->size = len; + der->data = gnutls_malloc (len); + if (der->data == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + result = asn1_der_coding (*pkey_info, "", der->data, &len, NULL); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto error; + } + + return 0; + + error: + asn1_delete_structure (pkey_info); + _gnutls_free_datum (&algo_params); + _gnutls_free_datum (&algo_privkey); + return result; + + } + +/* Converts a PKCS #8 private key info to + * a PKCS #8 EncryptedPrivateKeyInfo. + */ +static int +encode_to_pkcs8_key (schema_id schema, const gnutls_datum_t * der_key, + const char *password, ASN1_TYPE * out) + { + int result; + gnutls_datum_t key = + { NULL, 0}; + gnutls_datum_t tmp = + { NULL, 0}; + ASN1_TYPE pkcs8_asn = ASN1_TYPE_EMPTY; + struct pbkdf2_params kdf_params; + struct pbe_enc_params enc_params; + + if ((result = + asn1_create_element (_gnutls_get_pkix (), + "PKIX1.pkcs-8-EncryptedPrivateKeyInfo", + &pkcs8_asn)) != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto error; + } + + /* Write the encryption schema OID + */ + switch (schema) + { + case PBES2: + result = + asn1_write_value (pkcs8_asn, "encryptionAlgorithm.algorithm", + PBES2_OID, 1); + break; + case PKCS12_3DES_SHA1: + result = + asn1_write_value (pkcs8_asn, "encryptionAlgorithm.algorithm", + PKCS12_PBE_3DES_SHA1_OID, 1); + break; + case PKCS12_ARCFOUR_SHA1: + result = + asn1_write_value (pkcs8_asn, "encryptionAlgorithm.algorithm", + PKCS12_PBE_ARCFOUR_SHA1_OID, 1); + break; + case PKCS12_RC2_40_SHA1: + result = + asn1_write_value (pkcs8_asn, "encryptionAlgorithm.algorithm", + PKCS12_PBE_RC2_40_SHA1_OID, 1); + break; + + } + + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto error; + } + + /* Generate a symmetric key. + */ + + result = generate_key (schema, password, &kdf_params, &enc_params, &key); + if (result < 0) + { + gnutls_assert (); + goto error; + } + + result = + write_schema_params (schema, pkcs8_asn, + "encryptionAlgorithm.parameters", &kdf_params, + &enc_params); + if (result < 0) + { + gnutls_assert (); + goto error; + } + + /* Parameters have been encoded. Now + * encrypt the Data. + */ + result = encrypt_data (der_key, &enc_params, &key, &tmp); + if (result < 0) + { + gnutls_assert (); + goto error; + } + + /* write the encrypted data. + */ + result = asn1_write_value (pkcs8_asn, "encryptedData", tmp.data, tmp.size); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto error; + } + + _gnutls_free_datum (&tmp); + _gnutls_free_datum (&key); + + *out = pkcs8_asn; + + return 0; + + error: + _gnutls_free_datum (&key); + _gnutls_free_datum (&tmp); + asn1_delete_structure (&pkcs8_asn); + return result; + } + +/** + * gnutls_x509_privkey_export_pkcs8 - This function will export the private key to PKCS8 format + * @key: Holds the key + * @format: the format of output params. One of PEM or DER. + * @password: the password that will be used to encrypt the key. + * @flags: an ORed sequence of gnutls_pkcs_encrypt_flags_t + * @output_data: will contain a private key PEM or DER encoded + * @output_data_size: holds the size of output_data (and will be + * replaced by the actual size of parameters) + * + * This function will export the private key to a PKCS8 structure. + * Both RSA and DSA keys can be exported. For DSA keys we use + * PKCS #11 definitions. If the flags do not specify the encryption + * cipher, then the default 3DES (PBES2) will be used. + * + * The @password can be either ASCII or UTF-8 in the default PBES2 + * encryption schemas, or ASCII for the PKCS12 schemas. + * + * If the buffer provided is not long enough to hold the output, then + * *output_data_size is updated and GNUTLS_E_SHORT_MEMORY_BUFFER will + * be returned. + * + * If the structure is PEM encoded, it will have a header + * of "BEGIN ENCRYPTED PRIVATE KEY" or "BEGIN PRIVATE KEY" if + * encryption is not used. + * + * Return value: In case of failure a negative value will be + * returned, and 0 on success. + * + **/ +int +gnutls_x509_privkey_export_pkcs8 (gnutls_x509_privkey_t key, + gnutls_x509_crt_fmt_t format, + const char *password, + unsigned int flags, + void *output_data, + size_t * output_data_size) + { + ASN1_TYPE pkcs8_asn, pkey_info; + int ret; + gnutls_datum_t tmp; + schema_id schema; + + if (key == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + /* Get the private key info + * tmp holds the DER encoding. + */ + ret = encode_to_private_key_info (key, &tmp, &pkey_info); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + if (flags & GNUTLS_PKCS_USE_PKCS12_3DES) + schema = PKCS12_3DES_SHA1; + else if (flags & GNUTLS_PKCS_USE_PKCS12_ARCFOUR) + schema = PKCS12_ARCFOUR_SHA1; + else if (flags & GNUTLS_PKCS_USE_PKCS12_RC2_40) + schema = PKCS12_RC2_40_SHA1; + else + schema = PBES2; + + if ((flags & GNUTLS_PKCS_PLAIN) || password == NULL) + { + _gnutls_free_datum (&tmp); + + ret = + _gnutls_x509_export_int (pkey_info, format, + PEM_UNENCRYPTED_PKCS8, + output_data, output_data_size); + + asn1_delete_structure (&pkey_info); + } + else + { + asn1_delete_structure (&pkey_info); /* we don't need it */ + + ret = encode_to_pkcs8_key (schema, &tmp, password, &pkcs8_asn); + _gnutls_free_datum (&tmp); + + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + ret = + _gnutls_x509_export_int (pkcs8_asn, format, PEM_PKCS8, + output_data, output_data_size); + + asn1_delete_structure (&pkcs8_asn); + } + + return ret; + } + +/* Read the parameters cipher, IV, salt etc using the given + * schema ID. + */ +static int +read_pkcs_schema_params (schema_id schema, const char *password, + const opaque * data, int data_size, + struct pbkdf2_params *kdf_params, + struct pbe_enc_params *enc_params) + { + ASN1_TYPE pbes2_asn = ASN1_TYPE_EMPTY; + int result; + gnutls_datum_t tmp; + + switch (schema) + { + + case PBES2: + + /* Now check the key derivation and the encryption + * functions. + */ + if ((result = + asn1_create_element (_gnutls_get_pkix (), + "PKIX1.pkcs-5-PBES2-params", + &pbes2_asn)) != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto error; + } + + /* Decode the parameters. + */ + result = asn1_der_decoding (&pbes2_asn, data, data_size, NULL); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto error; + } + + tmp.data = (opaque *) data; + tmp.size = data_size; + + result = read_pbkdf2_params (pbes2_asn, &tmp, kdf_params); + if (result < 0) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto error; + } + + result = read_pbe_enc_params (pbes2_asn, &tmp, enc_params); + if (result < 0) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto error; + } + + asn1_delete_structure (&pbes2_asn); + return 0; + break; + + case PKCS12_3DES_SHA1: + case PKCS12_ARCFOUR_SHA1: + case PKCS12_RC2_40_SHA1: + + if ((schema) == PKCS12_3DES_SHA1) + { + enc_params->cipher = GNUTLS_CIPHER_3DES_CBC; + enc_params->iv_size = 8; + } + else if ((schema) == PKCS12_ARCFOUR_SHA1) + { + enc_params->cipher = GNUTLS_CIPHER_ARCFOUR_128; + enc_params->iv_size = 0; + } + else if ((schema) == PKCS12_RC2_40_SHA1) + { + enc_params->cipher = GNUTLS_CIPHER_RC2_40_CBC; + enc_params->iv_size = 8; + } + + if ((result = + asn1_create_element (_gnutls_get_pkix (), + "PKIX1.pkcs-12-PbeParams", + &pbes2_asn)) != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto error; + } + + /* Decode the parameters. + */ + result = asn1_der_decoding (&pbes2_asn, data, data_size, NULL); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto error; + } + + result = read_pkcs12_kdf_params (pbes2_asn, kdf_params); + if (result < 0) + { + gnutls_assert (); + goto error; + } + + if (enc_params->iv_size) + { + result = + _pkcs12_string_to_key (2 /*IV*/, kdf_params->salt, + kdf_params->salt_size, + kdf_params->iter_count, password, + enc_params->iv_size, enc_params->iv); + if (result < 0) + { + gnutls_assert (); + goto error; + } + + } + + asn1_delete_structure (&pbes2_asn); + + return 0; + break; + + } /* switch */ + + return GNUTLS_E_UNKNOWN_CIPHER_TYPE; + + error: + asn1_delete_structure (&pbes2_asn); + return result; + } + +/* Converts a PKCS #8 key to + * an internal structure (gnutls_private_key) + * (normally a PKCS #1 encoded RSA key) + */ +static int +decode_pkcs8_key (const gnutls_datum_t * raw_key, + const char *password, gnutls_x509_privkey_t pkey) + { + int result, len; + char enc_oid[64]; + gnutls_datum_t tmp; + ASN1_TYPE pbes2_asn = ASN1_TYPE_EMPTY, pkcs8_asn = ASN1_TYPE_EMPTY; + int params_start, params_end, params_len; + struct pbkdf2_params kdf_params; + struct pbe_enc_params enc_params; + schema_id schema; + + if ((result = + asn1_create_element (_gnutls_get_pkix (), + "PKIX1.pkcs-8-EncryptedPrivateKeyInfo", + &pkcs8_asn)) != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto error; + } + + result = asn1_der_decoding (&pkcs8_asn, raw_key->data, raw_key->size, NULL); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto error; + } + + /* Check the encryption schema OID + */ + len = sizeof (enc_oid); + result = + asn1_read_value (pkcs8_asn, "encryptionAlgorithm.algorithm", + enc_oid, &len); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + goto error; + } + + if ((result = check_schema (enc_oid)) < 0) + { + gnutls_assert (); + goto error; + } + + schema = result; + + /* Get the DER encoding of the parameters. + */ + result = + asn1_der_decoding_startEnd (pkcs8_asn, raw_key->data, + raw_key->size, + "encryptionAlgorithm.parameters", + ¶ms_start, ¶ms_end); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto error; + } + params_len = params_end - params_start + 1; + + result = + read_pkcs_schema_params (schema, password, + &raw_key->data[params_start], + params_len, &kdf_params, &enc_params); + + /* Parameters have been decoded. Now + * decrypt the EncryptedData. + */ + result = + decrypt_data (schema, pkcs8_asn, "encryptedData", password, + &kdf_params, &enc_params, &tmp); + if (result < 0) + { + gnutls_assert (); + goto error; + } + + asn1_delete_structure (&pkcs8_asn); + + result = decode_private_key_info (&tmp, pkey); + _gnutls_free_datum (&tmp); + + if (result < 0) + { + /* We've gotten this far. In the real world it's almost certain + * that we're dealing with a good file, but wrong password. + * Sadly like 90% of random data is somehow valid DER for the + * a first small number of bytes, so no easy way to guarantee. */ + if (result == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND || + result == GNUTLS_E_ASN1_IDENTIFIER_NOT_FOUND || + result == GNUTLS_E_ASN1_DER_ERROR || + result == GNUTLS_E_ASN1_VALUE_NOT_FOUND || + result == GNUTLS_E_ASN1_GENERIC_ERROR || + result == GNUTLS_E_ASN1_VALUE_NOT_VALID || + result == GNUTLS_E_ASN1_TAG_ERROR || + result == GNUTLS_E_ASN1_TAG_IMPLICIT || + result == GNUTLS_E_ASN1_TYPE_ANY_ERROR || + result == GNUTLS_E_ASN1_SYNTAX_ERROR || + result == GNUTLS_E_ASN1_DER_OVERFLOW) + { + result = GNUTLS_E_DECRYPTION_FAILED; + } + + gnutls_assert (); + goto error; + } + + return 0; + + error: + asn1_delete_structure (&pbes2_asn); + asn1_delete_structure (&pkcs8_asn); + return result; + } + +/* Decodes an RSA privateKey from a PKCS8 structure. + */ +static int +_decode_pkcs8_rsa_key (ASN1_TYPE pkcs8_asn, gnutls_x509_privkey pkey) + { + int ret; + gnutls_datum tmp; + + ret = _gnutls_x509_read_value (pkcs8_asn, "privateKey", &tmp, 0); + if (ret < 0) + { + gnutls_assert (); + goto error; + } + + pkey->key = _gnutls_privkey_decode_pkcs1_rsa_key (&tmp, pkey); + _gnutls_free_datum (&tmp); + if (pkey->key == NULL) + { + gnutls_assert (); + goto error; + } + + return 0; + + error: + gnutls_x509_privkey_deinit (pkey); + return ret; + } + +/* Decodes an DSA privateKey and params from a PKCS8 structure. + */ +static int +_decode_pkcs8_dsa_key (ASN1_TYPE pkcs8_asn, gnutls_x509_privkey pkey) + { + int ret; + gnutls_datum tmp; + + ret = _gnutls_x509_read_value (pkcs8_asn, "privateKey", &tmp, 0); + if (ret < 0) + { + gnutls_assert (); + goto error; + } + + ret = _gnutls_x509_read_der_int (tmp.data, tmp.size, &pkey->params[4]); + _gnutls_free_datum (&tmp); + + if (ret < 0) + { + gnutls_assert (); + goto error; + } + + ret = + _gnutls_x509_read_value (pkcs8_asn, "privateKeyAlgorithm.parameters", + &tmp, 0); + if (ret < 0) + { + gnutls_assert (); + goto error; + } + + ret = _gnutls_x509_read_dsa_params (tmp.data, tmp.size, pkey->params); + _gnutls_free_datum (&tmp); + if (ret < 0) + { + gnutls_assert (); + goto error; + } + + /* the public key can be generated as g^x mod p */ + pkey->params[3] = _gnutls_mpi_alloc_like (pkey->params[0]); + if (pkey->params[3] == NULL) + { + gnutls_assert (); + goto error; + } + + _gnutls_mpi_powm (pkey->params[3], pkey->params[2], pkey->params[4], + pkey->params[0]); + + if (!pkey->crippled) + { + ret = _gnutls_asn1_encode_dsa (&pkey->key, pkey->params); + if (ret < 0) + { + gnutls_assert (); + goto error; + } + } + + pkey->params_size = DSA_PRIVATE_PARAMS; + + return 0; + + error: + gnutls_x509_privkey_deinit (pkey); + return ret; + } + +static int +decode_private_key_info (const gnutls_datum_t * der, + gnutls_x509_privkey_t pkey) + { + int result, len; + opaque oid[64]; + ASN1_TYPE pkcs8_asn = ASN1_TYPE_EMPTY; + + if ((result = + asn1_create_element (_gnutls_get_pkix (), + "PKIX1.pkcs-8-PrivateKeyInfo", + &pkcs8_asn)) != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto error; + } + + result = asn1_der_decoding (&pkcs8_asn, der->data, der->size, NULL); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto error; + } + + /* Check the private key algorithm OID + */ + len = sizeof (oid); + result = + asn1_read_value (pkcs8_asn, "privateKeyAlgorithm.algorithm", oid, &len); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto error; + } + + /* we only support RSA and DSA private keys. + */ + if (strcmp (oid, PK_PKIX1_RSA_OID) == 0) + pkey->pk_algorithm = GNUTLS_PK_RSA; + else + { + gnutls_assert (); + _gnutls_x509_log + ("PKCS #8 private key OID '%s' is unsupported.\n", oid); + result = GNUTLS_E_UNKNOWN_PK_ALGORITHM; + goto error; + } + + /* Get the DER encoding of the actual private key. + */ + + if (pkey->pk_algorithm == GNUTLS_PK_RSA) + result = _decode_pkcs8_rsa_key (pkcs8_asn, pkey); + if (result < 0) + { + gnutls_assert (); + return result; + } + + result = 0; + + error: + asn1_delete_structure (&pkcs8_asn); + + return result; + + } + +/** + * gnutls_x509_privkey_import_pkcs8 - This function will import a DER or PEM PKCS8 encoded key + * @key: The structure to store the parsed key + * @data: The DER or PEM encoded key. + * @format: One of DER or PEM + * @password: the password to decrypt the key (if it is encrypted). + * @flags: 0 if encrypted or GNUTLS_PKCS_PLAIN if not encrypted. + * + * This function will convert the given DER or PEM encoded PKCS8 2.0 encrypted key + * to the native gnutls_x509_privkey_t format. The output will be stored in @key. + * Both RSA and DSA keys can be imported, and flags can only be used to indicate + * an unencrypted key. + * + * The @password can be either ASCII or UTF-8 in the default PBES2 + * encryption schemas, or ASCII for the PKCS12 schemas. + * + * If the Certificate is PEM encoded it should have a header of "ENCRYPTED PRIVATE KEY", + * or "PRIVATE KEY". You only need to specify the flags if the key is DER encoded, since + * in that case the encryption status cannot be auto-detected. + * + * Returns 0 on success. + * + **/ +int +gnutls_x509_privkey_import_pkcs8 (gnutls_x509_privkey_t key, + const gnutls_datum_t * data, + gnutls_x509_crt_fmt_t format, + const char *password, unsigned int flags) + { + int result = 0, need_free = 0; + gnutls_datum_t _data; + + if (key == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + _data.data = data->data; + _data.size = data->size; + + key->pk_algorithm = GNUTLS_PK_UNKNOWN; + + /* If the Certificate is in PEM format then decode it + */ + if (format == GNUTLS_X509_FMT_PEM) + { + opaque *out; + + /* Try the first header + */ + result = + _gnutls_fbase64_decode (PEM_UNENCRYPTED_PKCS8, + data->data, data->size, &out); + + if (result < 0) + { /* Try the encrypted header + */ + result = + _gnutls_fbase64_decode (PEM_PKCS8, data->data, data->size, &out); + + if (result <= 0) + { + if (result == 0) + result = GNUTLS_E_INTERNAL_ERROR; + gnutls_assert (); + return result; + } + } + else if (flags == 0) + flags |= GNUTLS_PKCS_PLAIN; + + _data.data = out; + _data.size = result; + + need_free = 1; + } + + if (flags & GNUTLS_PKCS_PLAIN) + { + result = decode_private_key_info (&_data, key); + } + else + { /* encrypted. */ + result = decode_pkcs8_key (&_data, password, key); + } + + if (result < 0) + { + gnutls_assert (); + goto cleanup; + } + + if (need_free) + _gnutls_free_datum (&_data); + + /* The key has now been decoded. + */ + + return 0; + + cleanup: + key->pk_algorithm = GNUTLS_PK_UNKNOWN; + if (need_free) + _gnutls_free_datum (&_data); + return result; + } + +/* Reads the PBKDF2 parameters. + */ +static int +read_pbkdf2_params (ASN1_TYPE pbes2_asn, + const gnutls_datum_t * der, struct pbkdf2_params *params) + { + int params_start, params_end; + int params_len, len, result; + ASN1_TYPE pbkdf2_asn = ASN1_TYPE_EMPTY; + char oid[64]; + + memset (params, 0, sizeof (params)); + + /* Check the key derivation algorithm + */ + len = sizeof (oid); + result = + asn1_read_value (pbes2_asn, "keyDerivationFunc.algorithm", oid, &len); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + _gnutls_hard_log ("keyDerivationFunc.algorithm: %s\n", oid); + + if (strcmp (oid, PBKDF2_OID) != 0) + { + gnutls_assert (); + _gnutls_x509_log + ("PKCS #8 key derivation OID '%s' is unsupported.\n", oid); + return _gnutls_asn2err (result); + } + + result = + asn1_der_decoding_startEnd (pbes2_asn, der->data, der->size, + "keyDerivationFunc.parameters", + ¶ms_start, ¶ms_end); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + params_len = params_end - params_start + 1; + + /* Now check the key derivation and the encryption + * functions. + */ + if ((result = + asn1_create_element (_gnutls_get_pkix (), + "PKIX1.pkcs-5-PBKDF2-params", + &pbkdf2_asn)) != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + result = + asn1_der_decoding (&pbkdf2_asn, &der->data[params_start], + params_len, NULL); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto error; + } + + /* read the salt */ + params->salt_size = sizeof (params->salt); + result = + asn1_read_value (pbkdf2_asn, "salt.specified", params->salt, + ¶ms->salt_size); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto error; + } + _gnutls_hard_log ("salt.specified.size: %d\n", params->salt_size); + + /* read the iteration count + */ + result = + _gnutls_x509_read_uint (pbkdf2_asn, "iterationCount", + ¶ms->iter_count); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + goto error; + } + _gnutls_hard_log ("iterationCount: %d\n", params->iter_count); + + /* read the keylength, if it is set. + */ + result = + _gnutls_x509_read_uint (pbkdf2_asn, "keyLength", ¶ms->key_size); + if (result < 0) + { + params->key_size = 0; + } + _gnutls_hard_log ("keyLength: %d\n", params->key_size); + + /* We don't read the PRF. We only use the default. + */ + + return 0; + + error: + asn1_delete_structure (&pbkdf2_asn); + return result; + + } + +/* Reads the PBE parameters from PKCS-12 schemas (*&#%*&#% RSA). + */ +static int +read_pkcs12_kdf_params (ASN1_TYPE pbes2_asn, struct pbkdf2_params *params) + { + int result; + + memset (params, 0, sizeof (params)); + + /* read the salt */ + params->salt_size = sizeof (params->salt); + result = + asn1_read_value (pbes2_asn, "salt", params->salt, ¶ms->salt_size); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto error; + } + _gnutls_hard_log ("salt.size: %d\n", params->salt_size); + + /* read the iteration count + */ + result = + _gnutls_x509_read_uint (pbes2_asn, "iterations", ¶ms->iter_count); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + goto error; + } + _gnutls_hard_log ("iterationCount: %d\n", params->iter_count); + + params->key_size = 0; + + return 0; + + error: + return result; + + } + +/* Writes the PBE parameters for PKCS-12 schemas. + */ +static int +write_pkcs12_kdf_params (ASN1_TYPE pbes2_asn, + const struct pbkdf2_params *kdf_params) + { + int result; + + /* write the salt + */ + result = + asn1_write_value (pbes2_asn, "salt", + kdf_params->salt, kdf_params->salt_size); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto error; + } + _gnutls_hard_log ("salt.size: %d\n", kdf_params->salt_size); + + /* write the iteration count + */ + result = + _gnutls_x509_write_uint32 (pbes2_asn, "iterations", + kdf_params->iter_count); + if (result < 0) + { + gnutls_assert (); + goto error; + } + _gnutls_hard_log ("iterationCount: %d\n", kdf_params->iter_count); + + return 0; + + error: + return result; + + } + +/* Converts an OID to a gnutls cipher type. + */ +inline static int +oid2cipher (const char *oid, gnutls_cipher_algorithm_t * algo) + { + + *algo = 0; + + if (strcmp (oid, DES_EDE3_CBC_OID) == 0) + { + *algo = GNUTLS_CIPHER_3DES_CBC; + return 0; + } + + if (strcmp (oid, DES_CBC_OID) == 0) + { + *algo = GNUTLS_CIPHER_DES_CBC; + return 0; + } + + _gnutls_x509_log ("PKCS #8 encryption OID '%s' is unsupported.\n", oid); + return GNUTLS_E_UNKNOWN_CIPHER_TYPE; + } + +static int +read_pbe_enc_params (ASN1_TYPE pbes2_asn, + const gnutls_datum_t * der, + struct pbe_enc_params *params) + { + int params_start, params_end; + int params_len, len, result; + ASN1_TYPE pbe_asn = ASN1_TYPE_EMPTY; + char oid[64]; + + memset (params, 0, sizeof (params)); + + /* Check the encryption algorithm + */ + len = sizeof (oid); + result = + asn1_read_value (pbes2_asn, "encryptionScheme.algorithm", oid, &len); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + goto error; + } + _gnutls_hard_log ("encryptionScheme.algorithm: %s\n", oid); + + if ((result = oid2cipher (oid, ¶ms->cipher)) < 0) + { + gnutls_assert (); + goto error; + } + + result = + asn1_der_decoding_startEnd (pbes2_asn, der->data, der->size, + "encryptionScheme.parameters", + ¶ms_start, ¶ms_end); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + params_len = params_end - params_start + 1; + + /* Now check the encryption parameters. + */ + if ((result = + asn1_create_element (_gnutls_get_pkix (), + "PKIX1.pkcs-5-des-EDE3-CBC-params", + &pbe_asn)) != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + result = + asn1_der_decoding (&pbe_asn, &der->data[params_start], params_len, NULL); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto error; + } + + /* read the IV */ + params->iv_size = sizeof (params->iv); + result = asn1_read_value (pbe_asn, "", params->iv, ¶ms->iv_size); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto error; + } + _gnutls_hard_log ("IV.size: %d\n", params->iv_size); + + return 0; + + error: + asn1_delete_structure (&pbe_asn); + return result; + + } + +static int +decrypt_data (schema_id schema, ASN1_TYPE pkcs8_asn, + const char *root, const char *password, + const struct pbkdf2_params *kdf_params, + const struct pbe_enc_params *enc_params, + gnutls_datum_t * decrypted_data) + { + int result; + int data_size; + opaque *data = NULL, *key = NULL; + gnutls_datum_t dkey, d_iv; + cipher_hd_t ch = NULL; + int key_size; + + data_size = 0; + result = asn1_read_value (pkcs8_asn, root, NULL, &data_size); + if (result != ASN1_MEM_ERROR) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + data = gnutls_malloc (data_size); + if (data == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + result = asn1_read_value (pkcs8_asn, root, data, &data_size); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto error; + } + + if (kdf_params->key_size == 0) + { + key_size = gnutls_cipher_get_key_size (enc_params->cipher); + } + else + key_size = kdf_params->key_size; + + key = gnutls_alloca (key_size); + if (key == NULL) + { + gnutls_assert (); + result = GNUTLS_E_MEMORY_ERROR; + goto error; + } + + /* generate the key + */ + if (schema == PBES2) + { + result = gc_pbkdf2_sha1 (password, strlen (password), + kdf_params->salt, kdf_params->salt_size, + kdf_params->iter_count, key, key_size); + + if (result != GC_OK) + { + gnutls_assert (); + result = GNUTLS_E_DECRYPTION_FAILED; + goto error; + } + } + else + { + result = + _pkcs12_string_to_key (1 /*KEY*/, kdf_params->salt, + kdf_params->salt_size, + kdf_params->iter_count, password, + key_size, key); + + if (result < 0) + { + gnutls_assert (); + goto error; + } + } + + /* do the decryption. + */ + dkey.data = key; + dkey.size = key_size; + + d_iv.data = (opaque *) enc_params->iv; + d_iv.size = enc_params->iv_size; + ch = _gnutls_cipher_init (enc_params->cipher, &dkey, &d_iv); + + gnutls_afree (key); + key = NULL; + + if (ch == NULL) + { + gnutls_assert (); + result = GNUTLS_E_DECRYPTION_FAILED; + goto error; + } + + result = _gnutls_cipher_decrypt (ch, data, data_size); + if (result < 0) + { + gnutls_assert (); + goto error; + } + + decrypted_data->data = data; + + if (_gnutls_cipher_get_block_size (enc_params->cipher) != 1) + decrypted_data->size = data_size - data[data_size - 1]; + else + decrypted_data->size = data_size; + + _gnutls_cipher_deinit (ch); + + return 0; + + error: + gnutls_free (data); + gnutls_afree (key); + if (ch != NULL) + _gnutls_cipher_deinit (ch); + return result; + } + +/* Writes the PBKDF2 parameters. + */ +static int +write_pbkdf2_params (ASN1_TYPE pbes2_asn, + const struct pbkdf2_params *kdf_params) + { + int result; + ASN1_TYPE pbkdf2_asn = ASN1_TYPE_EMPTY; + opaque tmp[64]; + + /* Write the key derivation algorithm + */ + result = + asn1_write_value (pbes2_asn, "keyDerivationFunc.algorithm", + PBKDF2_OID, 1); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + /* Now write the key derivation and the encryption + * functions. + */ + if ((result = + asn1_create_element (_gnutls_get_pkix (), + "PKIX1.pkcs-5-PBKDF2-params", + &pbkdf2_asn)) != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + result = asn1_write_value (pbkdf2_asn, "salt", "specified", 1); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto error; + } + + /* write the salt + */ + result = + asn1_write_value (pbkdf2_asn, "salt.specified", + kdf_params->salt, kdf_params->salt_size); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto error; + } + _gnutls_hard_log ("salt.specified.size: %d\n", kdf_params->salt_size); + + /* write the iteration count + */ + _gnutls_write_uint32 (kdf_params->iter_count, tmp); + + result = asn1_write_value (pbkdf2_asn, "iterationCount", tmp, 4); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto error; + } + _gnutls_hard_log ("iterationCount: %d\n", kdf_params->iter_count); + + /* write the keylength, if it is set. + */ + result = asn1_write_value (pbkdf2_asn, "keyLength", NULL, 0); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto error; + } + + /* We write an emptry prf. + */ + result = asn1_write_value (pbkdf2_asn, "prf", NULL, 0); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto error; + } + + /* now encode them an put the DER output + * in the keyDerivationFunc.parameters + */ + result = _gnutls_x509_der_encode_and_copy (pbkdf2_asn, "", + pbes2_asn, + "keyDerivationFunc.parameters", + 0); + if (result < 0) + { + gnutls_assert (); + goto error; + } + + return 0; + + error: + asn1_delete_structure (&pbkdf2_asn); + return result; + + } + +static int +write_pbe_enc_params (ASN1_TYPE pbes2_asn, + const struct pbe_enc_params *params) + { + int result; + ASN1_TYPE pbe_asn = ASN1_TYPE_EMPTY; + + /* Write the encryption algorithm + */ + result = + asn1_write_value (pbes2_asn, "encryptionScheme.algorithm", + DES_EDE3_CBC_OID, 1); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + goto error; + } + _gnutls_hard_log ("encryptionScheme.algorithm: %s\n", DES_EDE3_CBC_OID); + + /* Now check the encryption parameters. + */ + if ((result = + asn1_create_element (_gnutls_get_pkix (), + "PKIX1.pkcs-5-des-EDE3-CBC-params", + &pbe_asn)) != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + /* read the salt */ + result = asn1_write_value (pbe_asn, "", params->iv, params->iv_size); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto error; + } + _gnutls_hard_log ("IV.size: %d\n", params->iv_size); + + /* now encode them an put the DER output + * in the encryptionScheme.parameters + */ + result = _gnutls_x509_der_encode_and_copy (pbe_asn, "", + pbes2_asn, + "encryptionScheme.parameters", + 0); + if (result < 0) + { + gnutls_assert (); + goto error; + } + + return 0; + + error: + asn1_delete_structure (&pbe_asn); + return result; + + } + +/* Generates a key and also stores the key parameters. + */ +static int +generate_key (schema_id schema, + const char *password, + struct pbkdf2_params *kdf_params, + struct pbe_enc_params *enc_params, gnutls_datum_t * key) + { + opaque rnd[2]; + int ret; + + /* We should use the flags here to use different + * encryption algorithms etc. + */ + + if (schema == PKCS12_ARCFOUR_SHA1) + enc_params->cipher = GNUTLS_CIPHER_ARCFOUR_128; + else if (schema == PKCS12_3DES_SHA1) + enc_params->cipher = GNUTLS_CIPHER_3DES_CBC; + else if (schema == PKCS12_RC2_40_SHA1) + enc_params->cipher = GNUTLS_CIPHER_RC2_40_CBC; + + if (gc_pseudo_random (rnd, 2) != GC_OK) + { + gnutls_assert (); + return GNUTLS_E_RANDOM_FAILED; + } + + /* generate salt */ + + if (schema == PBES2) + { + kdf_params->salt_size = MIN (sizeof (kdf_params->salt), (unsigned) (10 + (rnd[1] % 10))); + } + else + kdf_params->salt_size = 8; + + if (gc_pseudo_random (kdf_params->salt, kdf_params->salt_size) != GC_OK) + { + gnutls_assert (); + return GNUTLS_E_RANDOM_FAILED; + } + + kdf_params->iter_count = 256 + rnd[0]; + key->size = kdf_params->key_size = + gnutls_cipher_get_key_size (enc_params->cipher); + + enc_params->iv_size = _gnutls_cipher_get_iv_size (enc_params->cipher); + + key->data = gnutls_secure_malloc (key->size); + if (key->data == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + /* now generate the key. + */ + + if (schema == PBES2) + { + + ret = gc_pbkdf2_sha1 (password, strlen (password), + kdf_params->salt, kdf_params->salt_size, + kdf_params->iter_count, + key->data, kdf_params->key_size); + if (ret != GC_OK) + { + gnutls_assert (); + return GNUTLS_E_ENCRYPTION_FAILED; + } + + if (enc_params->iv_size && + gc_nonce (enc_params->iv, enc_params->iv_size) != GC_OK) + { + gnutls_assert (); + return GNUTLS_E_RANDOM_FAILED; + } + } + else + { /* PKCS12 schemas */ + ret = + _pkcs12_string_to_key (1 /*KEY*/, kdf_params->salt, + kdf_params->salt_size, + kdf_params->iter_count, password, + kdf_params->key_size, key->data); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + /* Now generate the IV + */ + if (enc_params->iv_size) + { + ret = + _pkcs12_string_to_key (2 /*IV*/, kdf_params->salt, + kdf_params->salt_size, + kdf_params->iter_count, password, + enc_params->iv_size, enc_params->iv); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + } + } + + return 0; + } + +/* Encodes the parameters to be written in the encryptionAlgorithm.parameters + * part. + */ +static int +write_schema_params (schema_id schema, ASN1_TYPE pkcs8_asn, + const char *where, + const struct pbkdf2_params *kdf_params, + const struct pbe_enc_params *enc_params) + { + int result; + ASN1_TYPE pbes2_asn = ASN1_TYPE_EMPTY; + + if (schema == PBES2) + { + if ((result = + asn1_create_element (_gnutls_get_pkix (), + "PKIX1.pkcs-5-PBES2-params", + &pbes2_asn)) != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + result = write_pbkdf2_params (pbes2_asn, kdf_params); + if (result < 0) + { + gnutls_assert (); + goto error; + } + + result = write_pbe_enc_params (pbes2_asn, enc_params); + if (result < 0) + { + gnutls_assert (); + goto error; + } + + result = _gnutls_x509_der_encode_and_copy (pbes2_asn, "", + pkcs8_asn, where, 0); + if (result < 0) + { + gnutls_assert (); + goto error; + } + + asn1_delete_structure (&pbes2_asn); + } + else + { /* PKCS12 schemas */ + + if ((result = + asn1_create_element (_gnutls_get_pkix (), + "PKIX1.pkcs-12-PbeParams", + &pbes2_asn)) != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto error; + } + + result = write_pkcs12_kdf_params (pbes2_asn, kdf_params); + if (result < 0) + { + gnutls_assert (); + goto error; + } + + result = _gnutls_x509_der_encode_and_copy (pbes2_asn, "", + pkcs8_asn, where, 0); + if (result < 0) + { + gnutls_assert (); + goto error; + } + + asn1_delete_structure (&pbes2_asn); + + } + + return 0; + + error: + asn1_delete_structure (&pbes2_asn); + return result; + + } + +static int +encrypt_data (const gnutls_datum_t * plain, + const struct pbe_enc_params *enc_params, + gnutls_datum_t * key, gnutls_datum_t * encrypted) + { + int result; + int data_size; + opaque *data = NULL; + gnutls_datum_t d_iv; + cipher_hd_t ch = NULL; + opaque pad, pad_size; + + pad_size = _gnutls_cipher_get_block_size (enc_params->cipher); + + if (pad_size == 1) /* stream */ + pad_size = 0; + + data = gnutls_malloc (plain->size + pad_size); + if (data == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + memcpy (data, plain->data, plain->size); + + if (pad_size> 0) + { + pad = pad_size - (plain->size % pad_size); + if (pad == 0) + pad = pad_size; + memset (&data[plain->size], pad, pad); + } + else + pad = 0; + + data_size = plain->size + pad; + + d_iv.data = (opaque *) enc_params->iv; + d_iv.size = enc_params->iv_size; + ch = _gnutls_cipher_init (enc_params->cipher, key, &d_iv); + + if (ch == GNUTLS_CIPHER_FAILED) + { + gnutls_assert (); + result = GNUTLS_E_ENCRYPTION_FAILED; + goto error; + } + + result = _gnutls_cipher_encrypt (ch, data, data_size); + if (result < 0) + { + gnutls_assert (); + goto error; + } + + encrypted->data = data; + encrypted->size = data_size; + + _gnutls_cipher_deinit (ch); + + return 0; + + error: + gnutls_free (data); + if (ch != NULL) + _gnutls_cipher_deinit (ch); + return result; + } + +/* Decrypts a PKCS #7 encryptedData. The output is allocated + * and stored in dec. + */ +int +_gnutls_pkcs7_decrypt_data (const gnutls_datum_t * data, + const char *password, gnutls_datum_t * dec) + { + int result, len; + char enc_oid[64]; + gnutls_datum_t tmp; + ASN1_TYPE pbes2_asn = ASN1_TYPE_EMPTY, pkcs7_asn = ASN1_TYPE_EMPTY; + int params_start, params_end, params_len; + struct pbkdf2_params kdf_params; + struct pbe_enc_params enc_params; + schema_id schema; + + if ((result = + asn1_create_element (_gnutls_get_pkix (), + "PKIX1.pkcs-7-EncryptedData", + &pkcs7_asn)) != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto error; + } + + result = asn1_der_decoding (&pkcs7_asn, data->data, data->size, NULL); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto error; + } + + /* Check the encryption schema OID + */ + len = sizeof (enc_oid); + result = + asn1_read_value (pkcs7_asn, + "encryptedContentInfo.contentEncryptionAlgorithm.algorithm", + enc_oid, &len); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto error; + } + + if ((result = check_schema (enc_oid)) < 0) + { + gnutls_assert (); + goto error; + } + schema = result; + + /* Get the DER encoding of the parameters. + */ + result = + asn1_der_decoding_startEnd (pkcs7_asn, data->data, data->size, + "encryptedContentInfo.contentEncryptionAlgorithm.parameters", + ¶ms_start, ¶ms_end); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto error; + } + params_len = params_end - params_start + 1; + + result = + read_pkcs_schema_params (schema, password, + &data->data[params_start], + params_len, &kdf_params, &enc_params); + if (result < ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto error; + } + + /* Parameters have been decoded. Now + * decrypt the EncryptedData. + */ + + result = + decrypt_data (schema, pkcs7_asn, + "encryptedContentInfo.encryptedContent", password, + &kdf_params, &enc_params, &tmp); + if (result < 0) + { + gnutls_assert (); + goto error; + } + + asn1_delete_structure (&pkcs7_asn); + + *dec = tmp; + + return 0; + + error: + asn1_delete_structure (&pbes2_asn); + asn1_delete_structure (&pkcs7_asn); + return result; + } + +/* Encrypts to a PKCS #7 encryptedData. The output is allocated + * and stored in enc. + */ +int +_gnutls_pkcs7_encrypt_data (schema_id schema, + const gnutls_datum_t * data, + const char *password, gnutls_datum_t * enc) + { + int result; + gnutls_datum_t key = + { NULL, 0}; + gnutls_datum_t tmp = + { NULL, 0}; + ASN1_TYPE pkcs7_asn = ASN1_TYPE_EMPTY; + struct pbkdf2_params kdf_params; + struct pbe_enc_params enc_params; + + if ((result = + asn1_create_element (_gnutls_get_pkix (), + "PKIX1.pkcs-7-EncryptedData", + &pkcs7_asn)) != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto error; + } + + /* Write the encryption schema OID + */ + switch (schema) + { + case PBES2: + result = + asn1_write_value (pkcs7_asn, + "encryptedContentInfo.contentEncryptionAlgorithm.algorithm", + PBES2_OID, 1); + break; + case PKCS12_3DES_SHA1: + result = + asn1_write_value (pkcs7_asn, + "encryptedContentInfo.contentEncryptionAlgorithm.algorithm", + PKCS12_PBE_3DES_SHA1_OID, 1); + break; + case PKCS12_ARCFOUR_SHA1: + result = + asn1_write_value (pkcs7_asn, + "encryptedContentInfo.contentEncryptionAlgorithm.algorithm", + PKCS12_PBE_ARCFOUR_SHA1_OID, 1); + break; + case PKCS12_RC2_40_SHA1: + result = + asn1_write_value (pkcs7_asn, + "encryptedContentInfo.contentEncryptionAlgorithm.algorithm", + PKCS12_PBE_RC2_40_SHA1_OID, 1); + break; + + } + + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto error; + } + + /* Generate a symmetric key. + */ + + result = generate_key (schema, password, &kdf_params, &enc_params, &key); + if (result < 0) + { + gnutls_assert (); + goto error; + } + + result = write_schema_params (schema, pkcs7_asn, + "encryptedContentInfo.contentEncryptionAlgorithm.parameters", + &kdf_params, &enc_params); + if (result < 0) + { + gnutls_assert (); + goto error; + } + + /* Parameters have been encoded. Now + * encrypt the Data. + */ + result = encrypt_data (data, &enc_params, &key, &tmp); + if (result < 0) + { + gnutls_assert (); + goto error; + } + + /* write the encrypted data. + */ + result = + asn1_write_value (pkcs7_asn, + "encryptedContentInfo.encryptedContent", tmp.data, + tmp.size); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto error; + } + + _gnutls_free_datum (&tmp); + _gnutls_free_datum (&key); + + /* Now write the rest of the pkcs-7 stuff. + */ + + result = _gnutls_x509_write_uint32 (pkcs7_asn, "version", 0); + if (result < 0) + { + gnutls_assert (); + goto error; + } + + result = + asn1_write_value (pkcs7_asn, "encryptedContentInfo.contentType", + DATA_OID, 1); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto error; + } + + result = asn1_write_value (pkcs7_asn, "unprotectedAttrs", NULL, 0); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto error; + } + + /* Now encode and copy the DER stuff. + */ + result = _gnutls_x509_der_encode (pkcs7_asn, "", enc, 0); + + asn1_delete_structure (&pkcs7_asn); + + if (result < 0) + { + gnutls_assert (); + goto error; + } + + error: + _gnutls_free_datum (&key); + _gnutls_free_datum (&tmp); + asn1_delete_structure (&pkcs7_asn); + return result; + } + +#endif diff --git a/src/daemon/https/x509/rfc2818.h b/src/daemon/https/x509/rfc2818.h new file mode 100644 index 00000000..c3399145 --- /dev/null +++ b/src/daemon/https/x509/rfc2818.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +int _gnutls_hostname_compare (const char *certname, const char *hostname); +#define MAX_CN 256 diff --git a/src/daemon/https/x509/rfc2818_hostname.c b/src/daemon/https/x509/rfc2818_hostname.c new file mode 100644 index 00000000..93a96589 --- /dev/null +++ b/src/daemon/https/x509/rfc2818_hostname.c @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2007 Free Software Foundation + * Copyright (C) 2002 Andrew McDonald + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +#include +#include +#include +#include +#include +#include + +/* compare hostname against certificate, taking account of wildcards + * return 1 on success or 0 on error + */ +int +_gnutls_hostname_compare (const char *certname, const char *hostname) +{ + const char *cmpstr1, *cmpstr2; + + if (strlen (certname) == 0 || strlen (hostname) == 0) + return 0; + + if (strlen (certname) > 2 && strncmp (certname, "*.", 2) == 0) + { + /* a wildcard certificate */ + + cmpstr1 = certname + 1; + + /* find the first dot in hostname, compare from there on */ + cmpstr2 = strchr (hostname, '.'); + + if (cmpstr2 == NULL) + { + /* error, the hostname we're connecting to is only a local part */ + return 0; + } + + if (strcasecmp (cmpstr1, cmpstr2) == 0) + { + return 1; + } + + return 0; + } + + if (strcasecmp (certname, hostname) == 0) + { + return 1; + } + + return 0; +} + +/** + * gnutls_x509_crt_check_hostname - This function compares the given hostname with the hostname in the certificate + * @cert: should contain an gnutls_x509_crt_t structure + * @hostname: A null terminated string that contains a DNS name + * + * This function will check if the given certificate's subject + * matches the given hostname. This is a basic implementation of the + * matching described in RFC2818 (HTTPS), which takes into account + * wildcards, and the DNSName/IPAddress subject alternative name PKIX + * extension. + * + * Returns non zero for a successful match, and zero on failure. + **/ +int +gnutls_x509_crt_check_hostname (gnutls_x509_crt_t cert, const char *hostname) +{ + + char dnsname[MAX_CN]; + size_t dnsnamesize; + int found_dnsname = 0; + int ret = 0; + int i = 0; + + /* try matching against: + * 1) a DNS name as an alternative name (subjectAltName) extension + * in the certificate + * 2) the common name (CN) in the certificate + * + * either of these may be of the form: *.domain.tld + * + * only try (2) if there is no subjectAltName extension of + * type dNSName + */ + + /* Check through all included subjectAltName extensions, comparing + * against all those of type dNSName. + */ + for (i = 0; !(ret < 0); i++) + { + + dnsnamesize = sizeof (dnsname); + ret = gnutls_x509_crt_get_subject_alt_name (cert, i, + dnsname, &dnsnamesize, + NULL); + + if (ret == GNUTLS_SAN_DNSNAME) + { + found_dnsname = 1; + if (_gnutls_hostname_compare (dnsname, hostname)) + { + return 1; + } + } + else if (ret == GNUTLS_SAN_IPADDRESS) + { + found_dnsname = 1; /* RFC 2818 is unclear whether the CN + should be compared for IP addresses + too, but we won't do it. */ + if (_gnutls_hostname_compare (dnsname, hostname)) + { + return 1; + } + } + } + + if (!found_dnsname) + { + /* not got the necessary extension, use CN instead + */ + dnsnamesize = sizeof (dnsname); + if (gnutls_x509_crt_get_dn_by_oid (cert, OID_X520_COMMON_NAME, 0, + 0, dnsname, &dnsnamesize) < 0) + { + /* got an error, can't find a name + */ + return 0; + } + + if (_gnutls_hostname_compare (dnsname, hostname)) + { + return 1; + } + } + + /* not found a matching name + */ + return 0; +} diff --git a/src/daemon/https/x509/sign.c b/src/daemon/https/x509/sign.c new file mode 100644 index 00000000..275fc3f7 --- /dev/null +++ b/src/daemon/https/x509/sign.c @@ -0,0 +1,346 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2006, 2007 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +/* All functions which relate to X.509 certificate signing stuff are + * included here + */ + +#include + +#ifdef ENABLE_PKI + +#include +#include +#include +#include +#include /* MAX */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Writes the digest information and the digest in a DER encoded + * structure. The digest info is allocated and stored into the info structure. + */ +static int +encode_ber_digest_info (gnutls_digest_algorithm_t hash, + const gnutls_datum_t * digest, gnutls_datum_t * info) +{ + ASN1_TYPE dinfo = ASN1_TYPE_EMPTY; + int result; + const char *algo; + + algo = _gnutls_x509_mac_to_oid ((gnutls_mac_algorithm_t) hash); + if (algo == NULL) + { + gnutls_assert (); + _gnutls_x509_log ("Hash algorithm: %d\n", hash); + return GNUTLS_E_UNKNOWN_PK_ALGORITHM; + } + + if ((result = asn1_create_element (_gnutls_get_gnutls_asn (), + "GNUTLS.DigestInfo", + &dinfo)) != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + result = asn1_write_value (dinfo, "digestAlgorithm.algorithm", algo, 1); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + asn1_delete_structure (&dinfo); + return _gnutls_asn2err (result); + } + + /* Write an ASN.1 NULL in the parameters field. This matches RFC + 3279 and RFC 4055, although is arguable incorrect from a historic + perspective (see those documents for more information). + Regardless of what is correct, this appears to be what most + implementations do. */ + result = asn1_write_value (dinfo, "digestAlgorithm.parameters", + "\x05\x00", 2); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + asn1_delete_structure (&dinfo); + return _gnutls_asn2err (result); + } + + result = asn1_write_value (dinfo, "digest", digest->data, digest->size); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + asn1_delete_structure (&dinfo); + return _gnutls_asn2err (result); + } + + info->size = 0; + asn1_der_coding (dinfo, "", NULL, &info->size, NULL); + + info->data = gnutls_malloc (info->size); + if (info->data == NULL) + { + gnutls_assert (); + asn1_delete_structure (&dinfo); + return GNUTLS_E_MEMORY_ERROR; + } + + result = asn1_der_coding (dinfo, "", info->data, &info->size, NULL); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + asn1_delete_structure (&dinfo); + return _gnutls_asn2err (result); + } + + asn1_delete_structure (&dinfo); + + return 0; +} + +/* if hash==MD5 then we do RSA-MD5 + * if hash==SHA then we do RSA-SHA + * params[0] is modulus + * params[1] is public key + */ +static int +pkcs1_rsa_sign (gnutls_digest_algorithm_t hash, const gnutls_datum_t * text, + mpi_t * params, int params_len, gnutls_datum_t * signature) +{ + int ret; + opaque _digest[MAX_HASH_SIZE]; + GNUTLS_HASH_HANDLE hd; + gnutls_datum_t digest, info; + + hd = _gnutls_hash_init (HASH2MAC (hash)); + if (hd == NULL) + { + gnutls_assert (); + return GNUTLS_E_HASH_FAILED; + } + + _gnutls_hash (hd, text->data, text->size); + _gnutls_hash_deinit (hd, _digest); + + digest.data = _digest; + digest.size = _gnutls_hash_get_algo_len (HASH2MAC (hash)); + + /* Encode the digest as a DigestInfo + */ + if ((ret = encode_ber_digest_info (hash, &digest, &info)) != 0) + { + gnutls_assert (); + return ret; + } + + if ((ret = + _gnutls_sign (GNUTLS_PK_RSA, params, params_len, &info, + signature)) < 0) + { + gnutls_assert (); + _gnutls_free_datum (&info); + return ret; + } + + _gnutls_free_datum (&info); + + return 0; +} + +/* Signs the given data using the parameters from the signer's + * private key. + * + * returns 0 on success. + * + * 'tbs' is the data to be signed + * 'signature' will hold the signature! + * 'hash' is only used in PKCS1 RSA signing. + */ +int +_gnutls_x509_sign (const gnutls_datum_t * tbs, + gnutls_digest_algorithm_t hash, + gnutls_x509_privkey_t signer, gnutls_datum_t * signature) +{ + int ret; + + switch (signer->pk_algorithm) + { + case GNUTLS_PK_RSA: + ret = + pkcs1_rsa_sign (hash, tbs, signer->params, signer->params_size, + signature); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + return 0; + break; + default: + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + } + +} + +/* This is the same as the _gnutls_x509_sign, but this one will decode + * the ASN1_TYPE given, and sign the DER data. Actually used to get the DER + * of the TBS and sign it on the fly. + */ +int +_gnutls_x509_sign_tbs (ASN1_TYPE cert, const char *tbs_name, + gnutls_digest_algorithm_t hash, + gnutls_x509_privkey_t signer, + gnutls_datum_t * signature) +{ + int result; + opaque *buf; + int buf_size; + gnutls_datum_t tbs; + + buf_size = 0; + asn1_der_coding (cert, tbs_name, NULL, &buf_size, NULL); + + buf = gnutls_alloca (buf_size); + if (buf == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + result = asn1_der_coding (cert, tbs_name, buf, &buf_size, NULL); + + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + gnutls_afree (buf); + return _gnutls_asn2err (result); + } + + tbs.data = buf; + tbs.size = buf_size; + + result = _gnutls_x509_sign (&tbs, hash, signer, signature); + gnutls_afree (buf); + + return result; +} + +/*- + * _gnutls_x509_pkix_sign - This function will sign a CRL or a certificate with a key + * @src: should contain an ASN1_TYPE + * @issuer: is the certificate of the certificate issuer + * @issuer_key: holds the issuer's private key + * + * This function will sign a CRL or a certificate with the issuer's private key, and + * will copy the issuer's information into the CRL or certificate. + * + * Returns 0 on success. + * + -*/ +int +_gnutls_x509_pkix_sign (ASN1_TYPE src, const char *src_name, + gnutls_digest_algorithm_t dig, + gnutls_x509_crt_t issuer, + gnutls_x509_privkey_t issuer_key) +{ + int result; + gnutls_datum_t signature; + char name[128]; + + /* Step 1. Copy the issuer's name into the certificate. + */ + _gnutls_str_cpy (name, sizeof (name), src_name); + _gnutls_str_cat (name, sizeof (name), ".issuer"); + + result = asn1_copy_node (src, name, issuer->cert, "tbsCertificate.subject"); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + /* Step 1.5. Write the signature stuff in the tbsCertificate. + */ + _gnutls_str_cpy (name, sizeof (name), src_name); + _gnutls_str_cat (name, sizeof (name), ".signature"); + + result = _gnutls_x509_write_sig_params (src, name, + issuer_key->pk_algorithm, dig, + issuer_key->params, + issuer_key->params_size); + if (result < 0) + { + gnutls_assert (); + return result; + } + + /* Step 2. Sign the certificate. + */ + result = _gnutls_x509_sign_tbs (src, src_name, dig, issuer_key, &signature); + + if (result < 0) + { + gnutls_assert (); + return result; + } + + /* write the signature (bits) + */ + result = + asn1_write_value (src, "signature", signature.data, signature.size * 8); + + _gnutls_free_datum (&signature); + + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + /* Step 3. Move up and write the AlgorithmIdentifier, which is also + * the same. + */ + + result = _gnutls_x509_write_sig_params (src, "signatureAlgorithm", + issuer_key->pk_algorithm, dig, + issuer_key->params, + issuer_key->params_size); + if (result < 0) + { + gnutls_assert (); + return result; + } + + return 0; +} + +#endif diff --git a/src/daemon/https/x509/sign.h b/src/daemon/https/x509/sign.h new file mode 100644 index 00000000..e1bf46d4 --- /dev/null +++ b/src/daemon/https/x509/sign.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +int _gnutls_x509_sign (const gnutls_datum_t * tbs, + gnutls_digest_algorithm_t hash, + gnutls_x509_privkey_t signer, + gnutls_datum_t * signature); +int _gnutls_x509_sign_tbs (ASN1_TYPE cert, const char *tbs_name, + gnutls_digest_algorithm_t hash, + gnutls_x509_privkey_t signer, + gnutls_datum_t * signature); +int _gnutls_x509_pkix_sign (ASN1_TYPE src, const char *src_name, + gnutls_digest_algorithm_t, + gnutls_x509_crt_t issuer, + gnutls_x509_privkey_t issuer_key); diff --git a/src/daemon/https/x509/verify.h b/src/daemon/https/x509/verify.h new file mode 100644 index 00000000..d7ca5151 --- /dev/null +++ b/src/daemon/https/x509/verify.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2003, 2004, 2005 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +#include "x509.h" + +int gnutls_x509_crt_is_issuer (gnutls_x509_crt_t cert, + gnutls_x509_crt_t issuer); +int _gnutls_x509_verify_signature (const gnutls_datum_t * tbs, + const gnutls_datum_t * signature, + gnutls_x509_crt_t issuer); +int _gnutls_x509_privkey_verify_signature (const gnutls_datum_t * tbs, + const gnutls_datum_t * signature, + gnutls_x509_privkey_t issuer); diff --git a/src/daemon/https/x509/x509-api.texi b/src/daemon/https/x509/x509-api.texi new file mode 100644 index 00000000..f4dd35b9 --- /dev/null +++ b/src/daemon/https/x509/x509-api.texi @@ -0,0 +1,2943 @@ + +@subheading gnutls_x509_crl_init +@anchor{gnutls_x509_crl_init} +@deftypefun {int} {gnutls_x509_crl_init} (gnutls_x509_crl_t * @var{crl}) +@var{crl}: The structure to be initialized + +This function will initialize a CRL structure. CRL stands for +Certificate Revocation List. A revocation list usually contains +lists of certificate serial numbers that have been revoked +by an Authority. The revocation lists are always signed with +the authority's private key. + +Returns 0 on success. +@end deftypefun + +@subheading gnutls_x509_crl_deinit +@anchor{gnutls_x509_crl_deinit} +@deftypefun {void} {gnutls_x509_crl_deinit} (gnutls_x509_crl_t @var{crl}) +@var{crl}: The structure to be initialized + +This function will deinitialize a CRL structure. +@end deftypefun + +@subheading gnutls_x509_crl_import +@anchor{gnutls_x509_crl_import} +@deftypefun {int} {gnutls_x509_crl_import} (gnutls_x509_crl_t @var{crl}, const gnutls_datum_t * @var{data}, gnutls_x509_crt_fmt_t @var{format}) +@var{crl}: The structure to store the parsed CRL. + +@var{data}: The DER or PEM encoded CRL. + +@var{format}: One of DER or PEM + +This function will convert the given DER or PEM encoded CRL +to the native gnutls_x509_crl_t format. The output will be stored in 'crl'. + +If the CRL is PEM encoded it should have a header of "X509 CRL". + +Returns 0 on success. +@end deftypefun + +@subheading gnutls_x509_crl_get_issuer_dn +@anchor{gnutls_x509_crl_get_issuer_dn} +@deftypefun {int} {gnutls_x509_crl_get_issuer_dn} (const gnutls_x509_crl_t @var{crl}, char * @var{buf}, size_t * @var{sizeof_buf}) +@var{crl}: should contain a gnutls_x509_crl_t structure + +@var{buf}: a pointer to a structure to hold the peer's name (may be null) + +@var{sizeof_buf}: initially holds the size of @code{buf} + +This function will copy the name of the CRL issuer in the provided buffer. The name +will be in the form "C=xxxx,O=yyyy,CN=zzzz" as described in RFC2253. The output +string will be ASCII or UTF-8 encoded, depending on the certificate data. + +If buf is null then only the size will be filled. + +Returns GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is not long enough, and +in that case the sizeof_buf will be updated with the required size, and +0 on success. +@end deftypefun + +@subheading gnutls_x509_crl_get_issuer_dn_by_oid +@anchor{gnutls_x509_crl_get_issuer_dn_by_oid} +@deftypefun {int} {gnutls_x509_crl_get_issuer_dn_by_oid} (gnutls_x509_crl_t @var{crl}, const char * @var{oid}, int @var{indx}, unsigned int @var{raw_flag}, void * @var{buf}, size_t * @var{sizeof_buf}) +@var{crl}: should contain a gnutls_x509_crl_t structure + +@var{oid}: holds an Object Identified in null terminated string + +@var{indx}: In case multiple same OIDs exist in the RDN, this specifies which to send. Use zero to get the first one. + +@var{raw_flag}: If non zero returns the raw DER data of the DN part. + +@var{buf}: a pointer to a structure to hold the peer's name (may be null) + +@var{sizeof_buf}: initially holds the size of @code{buf} + +This function will extract the part of the name of the CRL issuer specified +by the given OID. The output will be encoded as described in RFC2253. The output +string will be ASCII or UTF-8 encoded, depending on the certificate data. + +Some helper macros with popular OIDs can be found in gnutls/x509.h +If raw flag is zero, this function will only return known OIDs as text. Other OIDs +will be DER encoded, as described in RFC2253 -- in hex format with a '\#' prefix. +You can check about known OIDs using @code{gnutls_x509_dn_oid_known()}. + +If buf is null then only the size will be filled. + +Returns GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is not long enough, and +in that case the sizeof_buf will be updated with the required size, +and 0 on success. +@end deftypefun + +@subheading gnutls_x509_crl_get_dn_oid +@anchor{gnutls_x509_crl_get_dn_oid} +@deftypefun {int} {gnutls_x509_crl_get_dn_oid} (gnutls_x509_crl_t @var{crl}, int @var{indx}, void * @var{oid}, size_t * @var{sizeof_oid}) +@var{crl}: should contain a gnutls_x509_crl_t structure + +@var{indx}: Specifies which DN OID to send. Use zero to get the first one. + +@var{oid}: a pointer to a structure to hold the name (may be null) + +@var{sizeof_oid}: initially holds the size of 'oid' + +This function will extract the requested OID of the name of the CRL issuer, specified +by the given index. + +If oid is null then only the size will be filled. + +Returns GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is not long enough, and +in that case the sizeof_oid will be updated with the required size. +On success 0 is returned. +@end deftypefun + +@subheading gnutls_x509_crl_get_signature_algorithm +@anchor{gnutls_x509_crl_get_signature_algorithm} +@deftypefun {int} {gnutls_x509_crl_get_signature_algorithm} (gnutls_x509_crl_t @var{crl}) +@var{crl}: should contain a gnutls_x509_crl_t structure + +This function will return a value of the gnutls_sign_algorithm_t enumeration that +is the signature algorithm. + +Returns a negative value on error. +@end deftypefun + +@subheading gnutls_x509_crl_get_signature +@anchor{gnutls_x509_crl_get_signature} +@deftypefun {int} {gnutls_x509_crl_get_signature} (gnutls_x509_crl_t @var{crl}, char * @var{sig}, size_t * @var{sizeof_sig}) +@var{crl}: should contain a gnutls_x509_crl_t structure + +@var{sig}: a pointer where the signature part will be copied (may be null). + +@var{sizeof_sig}: initially holds the size of @code{sig} + +This function will extract the signature field of a CRL. + +Returns 0 on success, and a negative value on error. +@end deftypefun + +@subheading gnutls_x509_crl_get_version +@anchor{gnutls_x509_crl_get_version} +@deftypefun {int} {gnutls_x509_crl_get_version} (gnutls_x509_crl_t @var{crl}) +@var{crl}: should contain a gnutls_x509_crl_t structure + +This function will return the version of the specified CRL. + +Returns a negative value on error. +@end deftypefun + +@subheading gnutls_x509_crl_get_this_update +@anchor{gnutls_x509_crl_get_this_update} +@deftypefun {time_t} {gnutls_x509_crl_get_this_update} (gnutls_x509_crl_t @var{crl}) +@var{crl}: should contain a gnutls_x509_crl_t structure + +This function will return the time this CRL was issued. + +Returns (time_t)-1 on error. +@end deftypefun + +@subheading gnutls_x509_crl_get_next_update +@anchor{gnutls_x509_crl_get_next_update} +@deftypefun {time_t} {gnutls_x509_crl_get_next_update} (gnutls_x509_crl_t @var{crl}) +@var{crl}: should contain a gnutls_x509_crl_t structure + +This function will return the time the next CRL will be issued. +This field is optional in a CRL so it might be normal to get +an error instead. + +Returns (time_t)-1 on error. +@end deftypefun + +@subheading gnutls_x509_crl_get_crt_count +@anchor{gnutls_x509_crl_get_crt_count} +@deftypefun {int} {gnutls_x509_crl_get_crt_count} (gnutls_x509_crl_t @var{crl}) +@var{crl}: should contain a gnutls_x509_crl_t structure + +This function will return the number of revoked certificates in the +given CRL. + +Returns a negative value on failure. +@end deftypefun + +@subheading gnutls_x509_crl_get_crt_serial +@anchor{gnutls_x509_crl_get_crt_serial} +@deftypefun {int} {gnutls_x509_crl_get_crt_serial} (gnutls_x509_crl_t @var{crl}, int @var{indx}, unsigned char * @var{serial}, size_t * @var{serial_size}, time_t * @var{t}) +@var{crl}: should contain a gnutls_x509_crl_t structure + +@var{indx}: the index of the certificate to extract (starting from 0) + +@var{serial}: where the serial number will be copied + +@var{serial_size}: initially holds the size of serial + +@var{t}: if non null, will hold the time this certificate was revoked + +This function will return the serial number of the specified, by +the index, revoked certificate. + +Returns a negative value on failure. +@end deftypefun + +@subheading gnutls_x509_crl_export +@anchor{gnutls_x509_crl_export} +@deftypefun {int} {gnutls_x509_crl_export} (gnutls_x509_crl_t @var{crl}, gnutls_x509_crt_fmt_t @var{format}, void * @var{output_data}, size_t * @var{output_data_size}) +@var{crl}: Holds the revocation list + +@var{format}: the format of output params. One of PEM or DER. + +@var{output_data}: will contain a private key PEM or DER encoded + +@var{output_data_size}: holds the size of output_data (and will be replaced by the actual size of parameters) + +This function will export the revocation list to DER or PEM format. + +If the buffer provided is not long enough to hold the output, then +GNUTLS_E_SHORT_MEMORY_BUFFER will be returned. + +If the structure is PEM encoded, it will have a header +of "BEGIN X509 CRL". + +Returns 0 on success, and a negative value on failure. +@end deftypefun + +@subheading gnutls_x509_rdn_get +@anchor{gnutls_x509_rdn_get} +@deftypefun {int} {gnutls_x509_rdn_get} (const gnutls_datum_t * @var{idn}, char * @var{buf}, size_t * @var{sizeof_buf}) +@var{idn}: should contain a DER encoded RDN sequence + +@var{buf}: a pointer to a structure to hold the peer's name + +@var{sizeof_buf}: holds the size of @code{buf} + +This function will return the name of the given RDN sequence. The +name will be in the form "C=xxxx,O=yyyy,CN=zzzz" as described in +RFC2253. + +If the provided buffer is not long enough, returns +GNUTLS_E_SHORT_MEMORY_BUFFER and *sizeof_buf will be updated. On +success 0 is returned. +@end deftypefun + +@subheading gnutls_x509_rdn_get_by_oid +@anchor{gnutls_x509_rdn_get_by_oid} +@deftypefun {int} {gnutls_x509_rdn_get_by_oid} (const gnutls_datum_t * @var{idn}, const char * @var{oid}, int @var{indx}, unsigned int @var{raw_flag}, void * @var{buf}, size_t * @var{sizeof_buf}) +@var{idn}: should contain a DER encoded RDN sequence + +@var{oid}: an Object Identifier + +@var{indx}: In case multiple same OIDs exist in the RDN indicates which +to send. Use 0 for the first one. + +@var{raw_flag}: If non zero then the raw DER data are returned. + +@var{buf}: a pointer to a structure to hold the peer's name + +@var{sizeof_buf}: holds the size of @code{buf} + +This function will return the name of the given Object identifier, +of the RDN sequence. The name will be encoded using the rules +from RFC2253. + +Returns GNUTLS_E_SHORT_MEMORY_BUFFER and updates *sizeof_buf if +the provided buffer is not long enough, and 0 on success. +@end deftypefun + +@subheading gnutls_x509_rdn_get_oid +@anchor{gnutls_x509_rdn_get_oid} +@deftypefun {int} {gnutls_x509_rdn_get_oid} (const gnutls_datum_t * @var{idn}, int @var{indx}, void * @var{buf}, size_t * @var{sizeof_buf}) +@var{idn}: should contain a DER encoded RDN sequence + +@var{indx}: Indicates which OID to return. Use 0 for the first one. + +This function will return the specified Object identifier, of the +RDN sequence. + +Returns GNUTLS_E_SHORT_MEMORY_BUFFER and updates *sizeof_buf if +the provided buffer is not long enough, and 0 on success. +@end deftypefun + +@subheading gnutls_x509_dn_oid_known +@anchor{gnutls_x509_dn_oid_known} +@deftypefun {int} {gnutls_x509_dn_oid_known} (const char * @var{oid}) +@var{oid}: holds an Object Identifier in a null terminated string + +This function will inform about known DN OIDs. This is useful since functions +like @code{gnutls_x509_crt_set_dn_by_oid()} use the information on known +OIDs to properly encode their input. Object Identifiers that are not +known are not encoded by these functions, and their input is stored directly +into the ASN.1 structure. In that case of unknown OIDs, you have +the responsibility of DER encoding your data. + +Returns 1 on known OIDs and 0 otherwise. +@end deftypefun + +@subheading gnutls_x509_crt_init +@anchor{gnutls_x509_crt_init} +@deftypefun {int} {gnutls_x509_crt_init} (gnutls_x509_crt_t * @var{cert}) +@var{cert}: The structure to be initialized + +This function will initialize an X.509 certificate structure. + +Returns 0 on success. +@end deftypefun + +@subheading gnutls_x509_crt_deinit +@anchor{gnutls_x509_crt_deinit} +@deftypefun {void} {gnutls_x509_crt_deinit} (gnutls_x509_crt_t @var{cert}) +@var{cert}: The structure to be initialized + +This function will deinitialize a CRL structure. +@end deftypefun + +@subheading gnutls_x509_crt_import +@anchor{gnutls_x509_crt_import} +@deftypefun {int} {gnutls_x509_crt_import} (gnutls_x509_crt_t @var{cert}, const gnutls_datum_t * @var{data}, gnutls_x509_crt_fmt_t @var{format}) +@var{cert}: The structure to store the parsed certificate. + +@var{data}: The DER or PEM encoded certificate. + +@var{format}: One of DER or PEM + +This function will convert the given DER or PEM encoded Certificate +to the native gnutls_x509_crt_t format. The output will be stored in @code{cert}. + +If the Certificate is PEM encoded it should have a header of "X509 CERTIFICATE", or +"CERTIFICATE". + +Returns 0 on success. +@end deftypefun + +@subheading gnutls_x509_crt_get_issuer_dn +@anchor{gnutls_x509_crt_get_issuer_dn} +@deftypefun {int} {gnutls_x509_crt_get_issuer_dn} (gnutls_x509_crt_t @var{cert}, char * @var{buf}, size_t * @var{sizeof_buf}) +@var{cert}: should contain a gnutls_x509_crt_t structure + +@var{buf}: a pointer to a structure to hold the name (may be null) + +@var{sizeof_buf}: initially holds the size of @code{buf} + +This function will copy the name of the Certificate issuer in the +provided buffer. The name will be in the form +"C=xxxx,O=yyyy,CN=zzzz" as described in RFC2253. The output string +will be ASCII or UTF-8 encoded, depending on the certificate data. + +If @code{buf} is null then only the size will be filled. + +Returns GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is not +long enough, and in that case the *sizeof_buf will be updated with +the required size. On success 0 is returned. +@end deftypefun + +@subheading gnutls_x509_crt_get_issuer_dn_by_oid +@anchor{gnutls_x509_crt_get_issuer_dn_by_oid} +@deftypefun {int} {gnutls_x509_crt_get_issuer_dn_by_oid} (gnutls_x509_crt_t @var{cert}, const char * @var{oid}, int @var{indx}, unsigned int @var{raw_flag}, void * @var{buf}, size_t * @var{sizeof_buf}) +@var{cert}: should contain a gnutls_x509_crt_t structure + +@var{oid}: holds an Object Identified in null terminated string + +@var{indx}: In case multiple same OIDs exist in the RDN, this specifies which to send. Use zero to get the first one. + +@var{raw_flag}: If non zero returns the raw DER data of the DN part. + +@var{buf}: a pointer to a structure to hold the name (may be null) + +@var{sizeof_buf}: initially holds the size of @code{buf} + +This function will extract the part of the name of the Certificate +issuer specified by the given OID. The output, if the raw flag is not +used, will be encoded as described in RFC2253. Thus a string that is +ASCII or UTF-8 encoded, depending on the certificate data. + +Some helper macros with popular OIDs can be found in gnutls/x509.h +If raw flag is zero, this function will only return known OIDs as +text. Other OIDs will be DER encoded, as described in RFC2253 -- +in hex format with a '\#' prefix. You can check about known OIDs +using @code{gnutls_x509_dn_oid_known()}. + +If @code{buf} is null then only the size will be filled. + +Returns GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is not +long enough, and in that case the *sizeof_buf will be updated with +the required size. On success 0 is returned. +@end deftypefun + +@subheading gnutls_x509_crt_get_issuer_dn_oid +@anchor{gnutls_x509_crt_get_issuer_dn_oid} +@deftypefun {int} {gnutls_x509_crt_get_issuer_dn_oid} (gnutls_x509_crt_t @var{cert}, int @var{indx}, void * @var{oid}, size_t * @var{sizeof_oid}) +@var{cert}: should contain a gnutls_x509_crt_t structure + +@var{indx}: This specifies which OID to return. Use zero to get the first one. + +@var{oid}: a pointer to a buffer to hold the OID (may be null) + +@var{sizeof_oid}: initially holds the size of @code{oid} + +This function will extract the OIDs of the name of the Certificate +issuer specified by the given index. + +If @code{oid} is null then only the size will be filled. + +Returns GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is not +long enough, and in that case the *sizeof_oid will be updated with +the required size. On success 0 is returned. +@end deftypefun + +@subheading gnutls_x509_crt_get_dn +@anchor{gnutls_x509_crt_get_dn} +@deftypefun {int} {gnutls_x509_crt_get_dn} (gnutls_x509_crt_t @var{cert}, char * @var{buf}, size_t * @var{sizeof_buf}) +@var{cert}: should contain a gnutls_x509_crt_t structure + +@var{buf}: a pointer to a structure to hold the name (may be null) + +@var{sizeof_buf}: initially holds the size of @code{buf} + +This function will copy the name of the Certificate in the +provided buffer. The name will be in the form +"C=xxxx,O=yyyy,CN=zzzz" as described in RFC2253. The output string +will be ASCII or UTF-8 encoded, depending on the certificate data. + +If @code{buf} is null then only the size will be filled. + +Returns GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is not +long enough, and in that case the *sizeof_buf will be updated with +the required size. On success 0 is returned. +@end deftypefun + +@subheading gnutls_x509_crt_get_dn_by_oid +@anchor{gnutls_x509_crt_get_dn_by_oid} +@deftypefun {int} {gnutls_x509_crt_get_dn_by_oid} (gnutls_x509_crt_t @var{cert}, const char * @var{oid}, int @var{indx}, unsigned int @var{raw_flag}, void * @var{buf}, size_t * @var{sizeof_buf}) +@var{cert}: should contain a gnutls_x509_crt_t structure + +@var{oid}: holds an Object Identified in null terminated string + +@var{indx}: In case multiple same OIDs exist in the RDN, this specifies which to send. Use zero to get the first one. + +@var{raw_flag}: If non zero returns the raw DER data of the DN part. + +@var{buf}: a pointer where the DN part will be copied (may be null). + +@var{sizeof_buf}: initially holds the size of @code{buf} + +This function will extract the part of the name of the Certificate +subject specified by the given OID. The output, if the raw flag is not +used, will be encoded as described in RFC2253. Thus a string that is +ASCII or UTF-8 encoded, depending on the certificate data. + +Some helper macros with popular OIDs can be found in gnutls/x509.h +If raw flag is zero, this function will only return known OIDs as +text. Other OIDs will be DER encoded, as described in RFC2253 -- +in hex format with a '\#' prefix. You can check about known OIDs +using @code{gnutls_x509_dn_oid_known()}. + +If @code{buf} is null then only the size will be filled. + +Returns GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is not +long enough, and in that case the *sizeof_buf will be updated with +the required size. On success 0 is returned. +@end deftypefun + +@subheading gnutls_x509_crt_get_dn_oid +@anchor{gnutls_x509_crt_get_dn_oid} +@deftypefun {int} {gnutls_x509_crt_get_dn_oid} (gnutls_x509_crt_t @var{cert}, int @var{indx}, void * @var{oid}, size_t * @var{sizeof_oid}) +@var{cert}: should contain a gnutls_x509_crt_t structure + +@var{indx}: This specifies which OID to return. Use zero to get the first one. + +@var{oid}: a pointer to a buffer to hold the OID (may be null) + +@var{sizeof_oid}: initially holds the size of @code{oid} + +This function will extract the OIDs of the name of the Certificate +subject specified by the given index. + +If oid is null then only the size will be filled. + +Returns GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is not +long enough, and in that case the *sizeof_oid will be updated with +the required size. On success 0 is returned. +@end deftypefun + +@subheading gnutls_x509_crt_get_signature_algorithm +@anchor{gnutls_x509_crt_get_signature_algorithm} +@deftypefun {int} {gnutls_x509_crt_get_signature_algorithm} (gnutls_x509_crt_t @var{cert}) +@var{cert}: should contain a gnutls_x509_crt_t structure + +This function will return a value of the gnutls_sign_algorithm_t enumeration that +is the signature algorithm. + +Returns a negative value on error. +@end deftypefun + +@subheading gnutls_x509_crt_get_signature +@anchor{gnutls_x509_crt_get_signature} +@deftypefun {int} {gnutls_x509_crt_get_signature} (gnutls_x509_crt_t @var{cert}, char * @var{sig}, size_t * @var{sizeof_sig}) +@var{cert}: should contain a gnutls_x509_crt_t structure + +@var{sig}: a pointer where the signature part will be copied (may be null). + +@var{sizeof_sig}: initially holds the size of @code{sig} + +This function will extract the signature field of a certificate. + +Returns 0 on success, and a negative value on error. +@end deftypefun + +@subheading gnutls_x509_crt_get_version +@anchor{gnutls_x509_crt_get_version} +@deftypefun {int} {gnutls_x509_crt_get_version} (gnutls_x509_crt_t @var{cert}) +@var{cert}: should contain a gnutls_x509_crt_t structure + +This function will return the version of the specified Certificate. + +Returns a negative value on error. +@end deftypefun + +@subheading gnutls_x509_crt_get_activation_time +@anchor{gnutls_x509_crt_get_activation_time} +@deftypefun {time_t} {gnutls_x509_crt_get_activation_time} (gnutls_x509_crt_t @var{cert}) +@var{cert}: should contain a gnutls_x509_crt_t structure + +This function will return the time this Certificate was or will be activated. + +Returns (time_t)-1 on error. +@end deftypefun + +@subheading gnutls_x509_crt_get_expiration_time +@anchor{gnutls_x509_crt_get_expiration_time} +@deftypefun {time_t} {gnutls_x509_crt_get_expiration_time} (gnutls_x509_crt_t @var{cert}) +@var{cert}: should contain a gnutls_x509_crt_t structure + +This function will return the time this Certificate was or will be expired. + +Returns (time_t)-1 on error. +@end deftypefun + +@subheading gnutls_x509_crt_get_serial +@anchor{gnutls_x509_crt_get_serial} +@deftypefun {int} {gnutls_x509_crt_get_serial} (gnutls_x509_crt_t @var{cert}, void * @var{result}, size_t * @var{result_size}) +@var{cert}: should contain a gnutls_x509_crt_t structure + +@var{result}: The place where the serial number will be copied + +@var{result_size}: Holds the size of the result field. + +This function will return the X.509 certificate's serial number. +This is obtained by the X509 Certificate serialNumber +field. Serial is not always a 32 or 64bit number. Some CAs use +large serial numbers, thus it may be wise to handle it as something +opaque. + +Returns 0 on success and a negative value in case of an error. +@end deftypefun + +@subheading gnutls_x509_crt_get_subject_key_id +@anchor{gnutls_x509_crt_get_subject_key_id} +@deftypefun {int} {gnutls_x509_crt_get_subject_key_id} (gnutls_x509_crt_t @var{cert}, void * @var{ret}, size_t * @var{ret_size}, unsigned int * @var{critical}) +@var{cert}: should contain a gnutls_x509_crt_t structure + +@var{ret}: The place where the identifier will be copied + +@var{ret_size}: Holds the size of the result field. + +@var{critical}: will be non zero if the extension is marked as critical (may be null) + +This function will return the X.509v3 certificate's subject key identifier. +This is obtained by the X.509 Subject Key identifier extension +field (2.5.29.14). + +Returns 0 on success and a negative value in case of an error. +@end deftypefun + +@subheading gnutls_x509_crt_get_authority_key_id +@anchor{gnutls_x509_crt_get_authority_key_id} +@deftypefun {int} {gnutls_x509_crt_get_authority_key_id} (gnutls_x509_crt_t @var{cert}, void * @var{ret}, size_t * @var{ret_size}, unsigned int * @var{critical}) +@var{cert}: should contain a gnutls_x509_crt_t structure + +@var{critical}: will be non zero if the extension is marked as critical (may be null) + +This function will return the X.509v3 certificate authority's key identifier. +This is obtained by the X.509 Authority Key identifier extension +field (2.5.29.35). Note that this function only returns the keyIdentifier +field of the extension. + +Returns 0 on success and a negative value in case of an error. +@end deftypefun + +@subheading gnutls_x509_crt_get_pk_algorithm +@anchor{gnutls_x509_crt_get_pk_algorithm} +@deftypefun {int} {gnutls_x509_crt_get_pk_algorithm} (gnutls_x509_crt_t @var{cert}, unsigned int * @var{bits}) +@var{cert}: should contain a gnutls_x509_crt_t structure + +@var{bits}: if bits is non null it will hold the size of the parameters' in bits + +This function will return the public key algorithm of an X.509 +certificate. + +If bits is non null, it should have enough size to hold the parameters +size in bits. For RSA the bits returned is the modulus. +For DSA the bits returned are of the public +exponent. + +Returns a member of the gnutls_pk_algorithm_t enumeration on success, +or a negative value on error. +@end deftypefun + +@subheading gnutls_x509_crt_get_subject_alt_name +@anchor{gnutls_x509_crt_get_subject_alt_name} +@deftypefun {int} {gnutls_x509_crt_get_subject_alt_name} (gnutls_x509_crt_t @var{cert}, unsigned int @var{seq}, void * @var{ret}, size_t * @var{ret_size}, unsigned int * @var{critical}) +@var{cert}: should contain a gnutls_x509_crt_t structure + +@var{seq}: specifies the sequence number of the alt name (0 for the first one, 1 for the second etc.) + +@var{ret}: is the place where the alternative name will be copied to + +@var{ret_size}: holds the size of ret. + +@var{critical}: will be non zero if the extension is marked as critical (may be null) + +This function will return the alternative names, contained in the +given certificate. + +This is specified in X509v3 Certificate Extensions. GNUTLS will +return the Alternative name (2.5.29.17), or a negative error code. + +When the SAN type is otherName, it will extract the data in the +otherName's value field, and @code{GNUTLS_SAN_OTHERNAME} is returned. +You may use @code{gnutls_x509_crt_get_subject_alt_othername_oid()} to get +the corresponding OID and the "virtual" SAN types (e.g., +@code{GNUTLS_SAN_OTHERNAME_XMPP}). + +If an otherName OID is known, the data will be decoded. Otherwise +the returned data will be DER encoded, and you will have to decode +it yourself. Currently, only the RFC 3920 id-on-xmppAddr SAN is +recognized. + +Returns the alternative subject name type on success. The type is +one of the enumerated gnutls_x509_subject_alt_name_t. It will +return @code{GNUTLS_E_SHORT_MEMORY_BUFFER} if @code{ret_size} is not large +enough to hold the value. In that case @code{ret_size} will be updated +with the required size. If the certificate does not have an +Alternative name with the specified sequence number then +@code{GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE} is returned. +@end deftypefun + +@subheading gnutls_x509_crt_get_subject_alt_name2 +@anchor{gnutls_x509_crt_get_subject_alt_name2} +@deftypefun {int} {gnutls_x509_crt_get_subject_alt_name2} (gnutls_x509_crt_t @var{cert}, unsigned int @var{seq}, void * @var{ret}, size_t * @var{ret_size}, unsigned int* @var{ret_type}, unsigned int * @var{critical}) +@var{cert}: should contain a gnutls_x509_crt_t structure + +@var{seq}: specifies the sequence number of the alt name (0 for the first one, 1 for the second etc.) + +@var{ret}: is the place where the alternative name will be copied to + +@var{ret_size}: holds the size of ret. + +@var{ret_type}: holds the type of the alternative name (one of gnutls_x509_subject_alt_name_t). + +@var{critical}: will be non zero if the extension is marked as critical (may be null) + +This function will return the alternative names, contained in the +given certificate. It is the same as @code{gnutls_x509_crt_get_subject_alt_name()} +except for the fact that it will return the type of the alternative +name in @code{ret_type} even if the function fails for some reason (i.e. +the buffer provided is not enough). + +The return values are the same as with @code{gnutls_x509_crt_get_subject_alt_name()}. +@end deftypefun + +@subheading gnutls_x509_crt_get_subject_alt_othername_oid +@anchor{gnutls_x509_crt_get_subject_alt_othername_oid} +@deftypefun {int} {gnutls_x509_crt_get_subject_alt_othername_oid} (gnutls_x509_crt_t @var{cert}, unsigned int @var{seq}, void * @var{ret}, size_t * @var{ret_size}) +@var{cert}: should contain a gnutls_x509_crt_t structure + +@var{seq}: specifies the sequence number of the alt name (0 for the first one, 1 for the second etc.) + +@var{ret}: is the place where the otherName OID will be copied to + +@var{ret_size}: holds the size of ret. + +This function will extract the type OID of an otherName Subject +Alternative Name, contained in the given certificate, and return +the type as an enumerated element. + +This function is only useful if +@code{gnutls_x509_crt_get_subject_alt_name()} returned +@code{GNUTLS_SAN_OTHERNAME}. + +Returns the alternative subject name type on success. The type is +one of the enumerated gnutls_x509_subject_alt_name_t. For +supported OIDs, it will return one of the virtual +(GNUTLS_SAN_OTHERNAME_*) types, e.g. @code{GNUTLS_SAN_OTHERNAME_XMPP}, +and @code{GNUTLS_SAN_OTHERNAME} for unknown OIDs. It will return +@code{GNUTLS_E_SHORT_MEMORY_BUFFER} if @code{ret_size} is not large enough to +hold the value. In that case @code{ret_size} will be updated with the +required size. If the certificate does not have an Alternative +name with the specified sequence number and with the otherName type +then @code{GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE} is returned. +@end deftypefun + +@subheading gnutls_x509_crt_get_basic_constraints +@anchor{gnutls_x509_crt_get_basic_constraints} +@deftypefun {int} {gnutls_x509_crt_get_basic_constraints} (gnutls_x509_crt_t @var{cert}, unsigned int * @var{critical}, int * @var{ca}, int * @var{pathlen}) +@var{cert}: should contain a gnutls_x509_crt_t structure + +@var{critical}: will be non zero if the extension is marked as critical + +@var{ca}: pointer to output integer indicating CA status, may be NULL, +value is 1 if the certificate CA flag is set, 0 otherwise. + +@var{pathlen}: pointer to output integer indicating path length (may be +NULL), non-negative values indicate a present pathLenConstraint +field and the actual value, -1 indicate that the field is absent. + +This function will read the certificate's basic constraints, and +return the certificates CA status. It reads the basicConstraints +X.509 extension (2.5.29.19). + +@strong{Return value:} If the certificate is a CA a positive value will be +returned, or zero if the certificate does not have CA flag set. A +negative value may be returned in case of errors. If the +certificate does not contain the basicConstraints extension +GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be returned. +@end deftypefun + +@subheading gnutls_x509_crt_get_ca_status +@anchor{gnutls_x509_crt_get_ca_status} +@deftypefun {int} {gnutls_x509_crt_get_ca_status} (gnutls_x509_crt_t @var{cert}, unsigned int * @var{critical}) +@var{cert}: should contain a gnutls_x509_crt_t structure + +@var{critical}: will be non zero if the extension is marked as critical + +This function will return certificates CA status, by reading the +basicConstraints X.509 extension (2.5.29.19). If the certificate is +a CA a positive value will be returned, or zero if the certificate +does not have CA flag set. + +Use @code{gnutls_x509_crt_get_basic_constraints()} if you want to read the +pathLenConstraint field too. + +A negative value may be returned in case of parsing error. +If the certificate does not contain the basicConstraints extension +GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be returned. +@end deftypefun + +@subheading gnutls_x509_crt_get_key_usage +@anchor{gnutls_x509_crt_get_key_usage} +@deftypefun {int} {gnutls_x509_crt_get_key_usage} (gnutls_x509_crt_t @var{cert}, unsigned int * @var{key_usage}, unsigned int * @var{critical}) +@var{cert}: should contain a gnutls_x509_crt_t structure + +@var{key_usage}: where the key usage bits will be stored + +@var{critical}: will be non zero if the extension is marked as critical + +This function will return certificate's key usage, by reading the +keyUsage X.509 extension (2.5.29.15). The key usage value will ORed values of the: +GNUTLS_KEY_DIGITAL_SIGNATURE, GNUTLS_KEY_NON_REPUDIATION, +GNUTLS_KEY_KEY_ENCIPHERMENT, GNUTLS_KEY_DATA_ENCIPHERMENT, +GNUTLS_KEY_KEY_AGREEMENT, GNUTLS_KEY_KEY_CERT_SIGN, +GNUTLS_KEY_CRL_SIGN, GNUTLS_KEY_ENCIPHER_ONLY, +GNUTLS_KEY_DECIPHER_ONLY. + +A negative value may be returned in case of parsing error. +If the certificate does not contain the keyUsage extension +GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be returned. +@end deftypefun + +@subheading gnutls_x509_crt_get_proxy +@anchor{gnutls_x509_crt_get_proxy} +@deftypefun {int} {gnutls_x509_crt_get_proxy} (gnutls_x509_crt_t @var{cert}, unsigned int * @var{critical}, int * @var{pathlen}, char ** @var{policyLanguage}, char ** @var{policy}, size_t * @var{sizeof_policy}) +@var{cert}: should contain a gnutls_x509_crt_t structure + +@var{critical}: will be non zero if the extension is marked as critical + +@var{pathlen}: pointer to output integer indicating path length (may be +NULL), non-negative values indicate a present pCPathLenConstraint +field and the actual value, -1 indicate that the field is absent. + +This function will read the certificate's basic constraints, and +return the certificates CA status. It reads the basicConstraints +X.509 extension (2.5.29.19). + +@strong{Return value:} If the certificate is a CA a positive value will be +returned, or zero if the certificate does not have CA flag set. A +negative value may be returned in case of errors. If the +certificate does not contain the basicConstraints extension +GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be returned. +@end deftypefun + +@subheading gnutls_x509_crt_get_extension_by_oid +@anchor{gnutls_x509_crt_get_extension_by_oid} +@deftypefun {int} {gnutls_x509_crt_get_extension_by_oid} (gnutls_x509_crt_t @var{cert}, const char * @var{oid}, int @var{indx}, void * @var{buf}, size_t * @var{sizeof_buf}, unsigned int * @var{critical}) +@var{cert}: should contain a gnutls_x509_crt_t structure + +@var{oid}: holds an Object Identified in null terminated string + +@var{indx}: In case multiple same OIDs exist in the extensions, this specifies which to send. Use zero to get the first one. + +@var{buf}: a pointer to a structure to hold the name (may be null) + +@var{sizeof_buf}: initially holds the size of @code{buf} + +@var{critical}: will be non zero if the extension is marked as critical + +This function will return the extension specified by the OID in the certificate. +The extensions will be returned as binary data DER encoded, in the provided +buffer. + +A negative value may be returned in case of parsing error. +If the certificate does not contain the specified extension +GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be returned. +@end deftypefun + +@subheading gnutls_x509_crt_get_extension_oid +@anchor{gnutls_x509_crt_get_extension_oid} +@deftypefun {int} {gnutls_x509_crt_get_extension_oid} (gnutls_x509_crt_t @var{cert}, int @var{indx}, void * @var{oid}, size_t * @var{sizeof_oid}) +@var{cert}: should contain a gnutls_x509_crt_t structure + +@var{indx}: Specifies which extension OID to send. Use zero to get the first one. + +@var{oid}: a pointer to a structure to hold the OID (may be null) + +@var{sizeof_oid}: initially holds the size of @code{oid} + +This function will return the requested extension OID in the certificate. +The extension OID will be stored as a string in the provided buffer. + +A negative value may be returned in case of parsing error. +If your have reached the last extension available +GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be returned. +@end deftypefun + +@subheading gnutls_x509_crt_get_extension_info +@anchor{gnutls_x509_crt_get_extension_info} +@deftypefun {int} {gnutls_x509_crt_get_extension_info} (gnutls_x509_crt_t @var{cert}, int @var{indx}, void * @var{oid}, size_t * @var{sizeof_oid}, int * @var{critical}) +@var{cert}: should contain a gnutls_x509_crt_t structure + +@var{indx}: Specifies which extension OID to send. Use zero to get the first one. + +@var{oid}: a pointer to a structure to hold the OID + +@var{sizeof_oid}: initially holds the size of @code{oid} + +@var{critical}: output variable with critical flag, may be NULL. + +This function will return the requested extension OID in the +certificate, and the critical flag for it. The extension OID will +be stored as a string in the provided buffer. Use +@code{gnutls_x509_crt_get_extension_data()} to extract the data. + +Return 0 on success. A negative value may be returned in case of +parsing error. If you have reached the last extension available +GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be returned. +@end deftypefun + +@subheading gnutls_x509_crt_get_extension_data +@anchor{gnutls_x509_crt_get_extension_data} +@deftypefun {int} {gnutls_x509_crt_get_extension_data} (gnutls_x509_crt_t @var{cert}, int @var{indx}, void * @var{data}, size_t * @var{sizeof_data}) +@var{cert}: should contain a gnutls_x509_crt_t structure + +@var{indx}: Specifies which extension OID to send. Use zero to get the first one. + +@var{data}: a pointer to a structure to hold the data (may be null) + +@var{sizeof_data}: initially holds the size of @code{oid} + +This function will return the requested extension data in the +certificate. The extension data will be stored as a string in the +provided buffer. + +Use @code{gnutls_x509_crt_get_extension_info()} to extract the OID and +critical flag. Use @code{gnutls_x509_crt_get_extension_by_oid()} instead, +if you want to get data indexed by the extension OID rather than +sequence. + +Return 0 on success. A negative value may be returned in case of +parsing error. If you have reached the last extension available +GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be returned. +@end deftypefun + +@subheading gnutls_x509_crt_get_raw_issuer_dn +@anchor{gnutls_x509_crt_get_raw_issuer_dn} +@deftypefun {int} {gnutls_x509_crt_get_raw_issuer_dn} (gnutls_x509_crt_t @var{cert}, gnutls_datum_t * @var{start}) +@var{cert}: should contain a gnutls_x509_crt_t structure + +@var{start}: will hold the starting point of the DN + +This function will return a pointer to the DER encoded DN structure +and the length. + +Returns 0 on success or a negative value on error. +@end deftypefun + +@subheading gnutls_x509_crt_get_raw_dn +@anchor{gnutls_x509_crt_get_raw_dn} +@deftypefun {int} {gnutls_x509_crt_get_raw_dn} (gnutls_x509_crt_t @var{cert}, gnutls_datum_t * @var{start}) +@var{cert}: should contain a gnutls_x509_crt_t structure + +@var{start}: will hold the starting point of the DN + +This function will return a pointer to the DER encoded DN structure and +the length. + +Returns 0 on success, or a negative value on error. +@end deftypefun + +@subheading gnutls_x509_crt_get_subject +@anchor{gnutls_x509_crt_get_subject} +@deftypefun {int} {gnutls_x509_crt_get_subject} (gnutls_x509_crt_t @var{cert}, gnutls_x509_dn_t * @var{dn}) +@var{cert}: should contain a gnutls_x509_crt_t structure + +@var{dn}: output variable with pointer to opaque DN. + +Return the Certificate's Subject DN as an opaque data type. You +may use @code{gnutls_x509_dn_get_rdn_ava()} to decode the DN. + +@strong{Returns:} Returns 0 on success, or an error code. +@end deftypefun + +@subheading gnutls_x509_crt_get_issuer +@anchor{gnutls_x509_crt_get_issuer} +@deftypefun {int} {gnutls_x509_crt_get_issuer} (gnutls_x509_crt_t @var{cert}, gnutls_x509_dn_t * @var{dn}) +@var{cert}: should contain a gnutls_x509_crt_t structure + +@var{dn}: output variable with pointer to opaque DN + +Return the Certificate's Issuer DN as an opaque data type. You may +use @code{gnutls_x509_dn_get_rdn_ava()} to decode the DN. + +Note that @code{dn} points into the @code{cert} object, and thus you may not +deallocate @code{cert} and continue to access @code{dn}. + +@strong{Returns:} Returns 0 on success, or an error code. +@end deftypefun + +@subheading gnutls_x509_dn_get_rdn_ava +@anchor{gnutls_x509_dn_get_rdn_ava} +@deftypefun {int} {gnutls_x509_dn_get_rdn_ava} (gnutls_x509_dn_t @var{dn}, int @var{irdn}, int @var{iava}, gnutls_x509_ava_st * @var{ava}) +@var{dn}: input variable with opaque DN pointer + +@var{irdn}: index of RDN + +@var{iava}: index of AVA. + +@var{ava}: Pointer to structure which will hold output information. + +Get pointers to data within the DN. + +Note that @code{ava} will contain pointers into the @code{dn} structure, so you +should not modify any data or deallocate it. Note also that the DN +in turn points into the original certificate structure, and thus +you may not deallocate the certificate and continue to access @code{dn}. + +@strong{Returns:} Returns 0 on success, or an error code. +@end deftypefun + +@subheading gnutls_x509_crt_get_fingerprint +@anchor{gnutls_x509_crt_get_fingerprint} +@deftypefun {int} {gnutls_x509_crt_get_fingerprint} (gnutls_x509_crt_t @var{cert}, gnutls_digest_algorithm_t @var{algo}, void * @var{buf}, size_t * @var{sizeof_buf}) +@var{cert}: should contain a gnutls_x509_crt_t structure + +@var{algo}: is a digest algorithm + +@var{buf}: a pointer to a structure to hold the fingerprint (may be null) + +@var{sizeof_buf}: initially holds the size of @code{buf} + +This function will calculate and copy the certificate's fingerprint +in the provided buffer. + +If the buffer is null then only the size will be filled. + +@strong{Returns:} @code{GNUTLS_E_SHORT_MEMORY_BUFFER} if the provided buffer is +not long enough, and in that case the *sizeof_buf will be updated +with the required size. On success 0 is returned. +@end deftypefun + +@subheading gnutls_x509_crt_export +@anchor{gnutls_x509_crt_export} +@deftypefun {int} {gnutls_x509_crt_export} (gnutls_x509_crt_t @var{cert}, gnutls_x509_crt_fmt_t @var{format}, void * @var{output_data}, size_t * @var{output_data_size}) +@var{cert}: Holds the certificate + +@var{format}: the format of output params. One of PEM or DER. + +@var{output_data}: will contain a certificate PEM or DER encoded + +@var{output_data_size}: holds the size of output_data (and will be +replaced by the actual size of parameters) + +This function will export the certificate to DER or PEM format. + +If the buffer provided is not long enough to hold the output, then +*output_data_size is updated and GNUTLS_E_SHORT_MEMORY_BUFFER will +be returned. + +If the structure is PEM encoded, it will have a header +of "BEGIN CERTIFICATE". + +@strong{Return value:} In case of failure a negative value will be +returned, and 0 on success. +@end deftypefun + +@subheading gnutls_x509_crt_get_key_id +@anchor{gnutls_x509_crt_get_key_id} +@deftypefun {int} {gnutls_x509_crt_get_key_id} (gnutls_x509_crt_t @var{crt}, unsigned int @var{flags}, unsigned char * @var{output_data}, size_t * @var{output_data_size}) +@var{crt}: Holds the certificate + +@var{flags}: should be 0 for now + +@var{output_data}: will contain the key ID + +@var{output_data_size}: holds the size of output_data (and will be +replaced by the actual size of parameters) + +This function will return a unique ID the depends on the public +key parameters. This ID can be used in checking whether a +certificate corresponds to the given private key. + +If the buffer provided is not long enough to hold the output, then +*output_data_size is updated and GNUTLS_E_SHORT_MEMORY_BUFFER will +be returned. The output will normally be a SHA-1 hash output, +which is 20 bytes. + +@strong{Return value:} In case of failure a negative value will be +returned, and 0 on success. +@end deftypefun + +@subheading gnutls_x509_crt_check_revocation +@anchor{gnutls_x509_crt_check_revocation} +@deftypefun {int} {gnutls_x509_crt_check_revocation} (gnutls_x509_crt_t @var{cert}, const gnutls_x509_crl_t * @var{crl_list}, int @var{crl_list_length}) +@var{cert}: should contain a gnutls_x509_crt_t structure + +@var{crl_list}: should contain a list of gnutls_x509_crl_t structures + +@var{crl_list_length}: the length of the crl_list + +This function will return check if the given certificate is +revoked. It is assumed that the CRLs have been verified before. + +@strong{Returns:} 0 if the certificate is NOT revoked, and 1 if it is. A +negative value is returned on error. +@end deftypefun + +@subheading gnutls_x509_crt_verify_data +@anchor{gnutls_x509_crt_verify_data} +@deftypefun {int} {gnutls_x509_crt_verify_data} (gnutls_x509_crt_t @var{crt}, unsigned int @var{flags}, const gnutls_datum_t * @var{data}, const gnutls_datum_t * @var{signature}) +@var{crt}: Holds the certificate + +@var{flags}: should be 0 for now + +@var{data}: holds the data to be signed + +@var{signature}: contains the signature + +This function will verify the given signed data, using the +parameters from the certificate. + +@strong{Returns:} In case of a verification failure 0 is returned, and 1 on +success. +@end deftypefun + +@subheading gnutls_x509_crt_get_crl_dist_points +@anchor{gnutls_x509_crt_get_crl_dist_points} +@deftypefun {int} {gnutls_x509_crt_get_crl_dist_points} (gnutls_x509_crt_t @var{cert}, unsigned int @var{seq}, void * @var{ret}, size_t * @var{ret_size}, unsigned int * @var{reason_flags}, unsigned int * @var{critical}) +@var{cert}: should contain a gnutls_x509_crt_t structure + +@var{seq}: specifies the sequence number of the distribution point (0 for the first one, 1 for the second etc.) + +@var{ret}: is the place where the distribution point will be copied to + +@var{ret_size}: holds the size of ret. + +@var{reason_flags}: Revocation reasons flags. + +@var{critical}: will be non zero if the extension is marked as critical (may be null) + +This function will return the CRL distribution points (2.5.29.31), +contained in the given certificate. + +@code{reason_flags} should be an ORed sequence of +GNUTLS_CRL_REASON_UNUSED, GNUTLS_CRL_REASON_KEY_COMPROMISE, +GNUTLS_CRL_REASON_CA_COMPROMISE, +GNUTLS_CRL_REASON_AFFILIATION_CHANGED, +GNUTLS_CRL_REASON_SUPERSEEDED, +GNUTLS_CRL_REASON_CESSATION_OF_OPERATION, +GNUTLS_CRL_REASON_CERTIFICATE_HOLD, +GNUTLS_CRL_REASON_PRIVILEGE_WITHDRAWN, +GNUTLS_CRL_REASON_AA_COMPROMISE, or zero for all possible reasons. + +This is specified in X509v3 Certificate Extensions. GNUTLS will +return the distribution point type, or a negative error code on +error. + +Returns @code{GNUTLS_E_SHORT_MEMORY_BUFFER} and updates &@code{ret_size} if +&@code{ret_size} is not enough to hold the distribution point, or the +type of the distribution point if everything was ok. The type is +one of the enumerated @code{gnutls_x509_subject_alt_name_t}. If the +certificate does not have an Alternative name with the specified +sequence number then @code{GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE} is +returned. +@end deftypefun + +@subheading gnutls_x509_crt_get_key_purpose_oid +@anchor{gnutls_x509_crt_get_key_purpose_oid} +@deftypefun {int} {gnutls_x509_crt_get_key_purpose_oid} (gnutls_x509_crt_t @var{cert}, int @var{indx}, void * @var{oid}, size_t * @var{sizeof_oid}, unsigned int * @var{critical}) +@var{cert}: should contain a gnutls_x509_crt_t structure + +@var{indx}: This specifies which OID to return. Use zero to get the first one. + +@var{oid}: a pointer to a buffer to hold the OID (may be null) + +@var{sizeof_oid}: initially holds the size of @code{oid} + +This function will extract the key purpose OIDs of the Certificate +specified by the given index. These are stored in the Extended Key +Usage extension (2.5.29.37) See the GNUTLS_KP_* definitions for +human readable names. + +If @code{oid} is null then only the size will be filled. + +@strong{Returns:} @code{GNUTLS_E_SHORT_MEMORY_BUFFER} if the provided buffer is +not long enough, and in that case the *sizeof_oid will be updated +with the required size. On success 0 is returned. +@end deftypefun + +@subheading gnutls_x509_crt_get_pk_rsa_raw +@anchor{gnutls_x509_crt_get_pk_rsa_raw} +@deftypefun {int} {gnutls_x509_crt_get_pk_rsa_raw} (gnutls_x509_crt_t @var{crt}, gnutls_datum_t * @var{m}, gnutls_datum_t * @var{e}) +@var{crt}: Holds the certificate + +@var{m}: will hold the modulus + +@var{e}: will hold the public exponent + +This function will export the RSA public key's parameters found in +the given structure. The new parameters will be allocated using +@code{gnutls_malloc()} and will be stored in the appropriate datum. + +@strong{Returns:} @code{GNUTLS_E_SUCCESS} on success, otherwise an error. +@end deftypefun + +@subheading gnutls_x509_crt_get_pk_dsa_raw +@anchor{gnutls_x509_crt_get_pk_dsa_raw} +@deftypefun {int} {gnutls_x509_crt_get_pk_dsa_raw} (gnutls_x509_crt_t @var{crt}, gnutls_datum_t * @var{p}, gnutls_datum_t * @var{q}, gnutls_datum_t * @var{g}, gnutls_datum_t * @var{y}) +@var{crt}: Holds the certificate + +@var{p}: will hold the p + +@var{q}: will hold the q + +@var{g}: will hold the g + +@var{y}: will hold the y + +This function will export the DSA public key's parameters found in +the given certificate. The new parameters will be allocated using +@code{gnutls_malloc()} and will be stored in the appropriate datum. + +@strong{Returns:} @code{GNUTLS_E_SUCCESS} on success, otherwise an error. +@end deftypefun + +@subheading gnutls_x509_crt_list_import +@anchor{gnutls_x509_crt_list_import} +@deftypefun {int} {gnutls_x509_crt_list_import} (gnutls_x509_crt_t * @var{certs}, unsigned int * @var{cert_max}, const gnutls_datum_t * @var{data}, gnutls_x509_crt_fmt_t @var{format}, unsigned int @var{flags}) +@var{certs}: The structures to store the parsed certificate. Must not be initialized. + +@var{cert_max}: Initially must hold the maximum number of certs. It will be updated with the number of certs available. + +@var{data}: The PEM encoded certificate. + +@var{format}: One of DER or PEM. + +@var{flags}: must be zero or an OR'd sequence of gnutls_certificate_import_flags. + +This function will convert the given PEM encoded certificate list +to the native gnutls_x509_crt_t format. The output will be stored +in @code{certs}. They will be automatically initialized. + +If the Certificate is PEM encoded it should have a header of "X509 +CERTIFICATE", or "CERTIFICATE". + +@strong{Returns:} the number of certificates read or a negative error value. +@end deftypefun + +@subheading gnutls_x509_crt_check_hostname +@anchor{gnutls_x509_crt_check_hostname} +@deftypefun {int} {gnutls_x509_crt_check_hostname} (gnutls_x509_crt_t @var{cert}, const char * @var{hostname}) +@var{cert}: should contain an gnutls_x509_crt_t structure + +@var{hostname}: A null terminated string that contains a DNS name + +This function will check if the given certificate's subject +matches the given hostname. This is a basic implementation of the +matching described in RFC2818 (HTTPS), which takes into account +wildcards, and the DNSName/IPAddress subject alternative name PKIX +extension. + +Returns non zero for a successful match, and zero on failure. +@end deftypefun + +@subheading gnutls_x509_crt_check_issuer +@anchor{gnutls_x509_crt_check_issuer} +@deftypefun {int} {gnutls_x509_crt_check_issuer} (gnutls_x509_crt_t @var{cert}, gnutls_x509_crt_t @var{issuer}) +@var{cert}: is the certificate to be checked + +@var{issuer}: is the certificate of a possible issuer + +This function will check if the given certificate was issued by the +given issuer. It will return true (1) if the given certificate is issued +by the given issuer, and false (0) if not. + +A negative value is returned in case of an error. +@end deftypefun + +@subheading gnutls_x509_crt_list_verify +@anchor{gnutls_x509_crt_list_verify} +@deftypefun {int} {gnutls_x509_crt_list_verify} (const gnutls_x509_crt_t * @var{cert_list}, int @var{cert_list_length}, const gnutls_x509_crt_t * @var{CA_list}, int @var{CA_list_length}, const gnutls_x509_crl_t * @var{CRL_list}, int @var{CRL_list_length}, unsigned int @var{flags}, unsigned int * @var{verify}) +@var{cert_list}: is the certificate list to be verified + +@var{cert_list_length}: holds the number of certificate in cert_list + +@var{CA_list}: is the CA list which will be used in verification + +@var{CA_list_length}: holds the number of CA certificate in CA_list + +@var{CRL_list}: holds a list of CRLs. + +@var{CRL_list_length}: the length of CRL list. + +@var{flags}: Flags that may be used to change the verification algorithm. Use OR of the gnutls_certificate_verify_flags enumerations. + +@var{verify}: will hold the certificate verification output. + +This function will try to verify the given certificate list and return its status. +Note that expiration and activation dates are not checked +by this function, you should check them using the appropriate functions. + +If no flags are specified (0), this function will use the +basicConstraints (2.5.29.19) PKIX extension. This means that only a certificate +authority is allowed to sign a certificate. + +You must also check the peer's name in order to check if the verified +certificate belongs to the actual peer. + +The certificate verification output will be put in @code{verify} and will be +one or more of the gnutls_certificate_status_t enumerated elements bitwise or'd. +For a more detailed verification status use @code{gnutls_x509_crt_verify()} per list +element. + +@strong{GNUTLS_CERT_INVALID:} the certificate chain is not valid. + +@strong{GNUTLS_CERT_REVOKED:} a certificate in the chain has been revoked. + +Returns 0 on success and a negative value in case of an error. +@end deftypefun + +@subheading gnutls_x509_crt_verify +@anchor{gnutls_x509_crt_verify} +@deftypefun {int} {gnutls_x509_crt_verify} (gnutls_x509_crt_t @var{cert}, const gnutls_x509_crt_t * @var{CA_list}, int @var{CA_list_length}, unsigned int @var{flags}, unsigned int * @var{verify}) +@var{cert}: is the certificate to be verified + +@var{CA_list}: is one certificate that is considered to be trusted one + +@var{CA_list_length}: holds the number of CA certificate in CA_list + +@var{flags}: Flags that may be used to change the verification algorithm. Use OR of the gnutls_certificate_verify_flags enumerations. + +@var{verify}: will hold the certificate verification output. + +This function will try to verify the given certificate and return its status. +The verification output in this functions cannot be GNUTLS_CERT_NOT_VALID. + +Returns 0 on success and a negative value in case of an error. +@end deftypefun + +@subheading gnutls_x509_crl_check_issuer +@anchor{gnutls_x509_crl_check_issuer} +@deftypefun {int} {gnutls_x509_crl_check_issuer} (gnutls_x509_crl_t @var{cert}, gnutls_x509_crt_t @var{issuer}) +@var{issuer}: is the certificate of a possible issuer + +This function will check if the given CRL was issued by the +given issuer certificate. It will return true (1) if the given CRL was issued +by the given issuer, and false (0) if not. + +A negative value is returned in case of an error. +@end deftypefun + +@subheading gnutls_x509_crl_verify +@anchor{gnutls_x509_crl_verify} +@deftypefun {int} {gnutls_x509_crl_verify} (gnutls_x509_crl_t @var{crl}, const gnutls_x509_crt_t * @var{CA_list}, int @var{CA_list_length}, unsigned int @var{flags}, unsigned int * @var{verify}) +@var{crl}: is the crl to be verified + +@var{CA_list}: is a certificate list that is considered to be trusted one + +@var{CA_list_length}: holds the number of CA certificates in CA_list + +@var{flags}: Flags that may be used to change the verification algorithm. Use OR of the gnutls_certificate_verify_flags enumerations. + +@var{verify}: will hold the crl verification output. + +This function will try to verify the given crl and return its status. +See @code{gnutls_x509_crt_list_verify()} for a detailed description of +return values. + +Returns 0 on success and a negative value in case of an error. +@end deftypefun + +@subheading gnutls_x509_privkey_init +@anchor{gnutls_x509_privkey_init} +@deftypefun {int} {gnutls_x509_privkey_init} (gnutls_x509_privkey_t * @var{key}) +@var{key}: The structure to be initialized + +This function will initialize an private key structure. + +Returns 0 on success. +@end deftypefun + +@subheading gnutls_x509_privkey_deinit +@anchor{gnutls_x509_privkey_deinit} +@deftypefun {void} {gnutls_x509_privkey_deinit} (gnutls_x509_privkey_t @var{key}) +@var{key}: The structure to be initialized + +This function will deinitialize a private key structure. +@end deftypefun + +@subheading gnutls_x509_privkey_cpy +@anchor{gnutls_x509_privkey_cpy} +@deftypefun {int} {gnutls_x509_privkey_cpy} (gnutls_x509_privkey_t @var{dst}, gnutls_x509_privkey_t @var{src}) +@var{dst}: The destination key, which should be initialized. + +@var{src}: The source key + +This function will copy a private key from source to destination key. +@end deftypefun + +@subheading gnutls_x509_privkey_import +@anchor{gnutls_x509_privkey_import} +@deftypefun {int} {gnutls_x509_privkey_import} (gnutls_x509_privkey_t @var{key}, const gnutls_datum_t * @var{data}, gnutls_x509_crt_fmt_t @var{format}) +@var{key}: The structure to store the parsed key + +@var{data}: The DER or PEM encoded certificate. + +@var{format}: One of DER or PEM + +This function will convert the given DER or PEM encoded key +to the native gnutls_x509_privkey_t format. The output will be stored in @code{key} . + +If the key is PEM encoded it should have a header of "RSA PRIVATE KEY", or +"DSA PRIVATE KEY". + +Returns 0 on success. +@end deftypefun + +@subheading gnutls_x509_privkey_import_rsa_raw +@anchor{gnutls_x509_privkey_import_rsa_raw} +@deftypefun {int} {gnutls_x509_privkey_import_rsa_raw} (gnutls_x509_privkey_t @var{key}, const gnutls_datum_t * @var{m}, const gnutls_datum_t * @var{e}, const gnutls_datum_t * @var{d}, const gnutls_datum_t * @var{p}, const gnutls_datum_t * @var{q}, const gnutls_datum_t * @var{u}) +@var{key}: The structure to store the parsed key + +@var{m}: holds the modulus + +@var{e}: holds the public exponent + +@var{d}: holds the private exponent + +@var{p}: holds the first prime (p) + +@var{q}: holds the second prime (q) + +@var{u}: holds the coefficient + +This function will convert the given RSA raw parameters +to the native gnutls_x509_privkey_t format. The output will be stored in @code{key}. +@end deftypefun + +@subheading gnutls_x509_privkey_import_dsa_raw +@anchor{gnutls_x509_privkey_import_dsa_raw} +@deftypefun {int} {gnutls_x509_privkey_import_dsa_raw} (gnutls_x509_privkey_t @var{key}, const gnutls_datum_t * @var{p}, const gnutls_datum_t * @var{q}, const gnutls_datum_t * @var{g}, const gnutls_datum_t * @var{y}, const gnutls_datum_t * @var{x}) +@var{key}: The structure to store the parsed key + +@var{p}: holds the p + +@var{q}: holds the q + +@var{g}: holds the g + +@var{y}: holds the y + +@var{x}: holds the x + +This function will convert the given DSA raw parameters +to the native gnutls_x509_privkey_t format. The output will be stored in @code{key}. +@end deftypefun + +@subheading gnutls_x509_privkey_get_pk_algorithm +@anchor{gnutls_x509_privkey_get_pk_algorithm} +@deftypefun {int} {gnutls_x509_privkey_get_pk_algorithm} (gnutls_x509_privkey_t @var{key}) +@var{key}: should contain a gnutls_x509_privkey_t structure + +This function will return the public key algorithm of a private +key. + +Returns a member of the gnutls_pk_algorithm_t enumeration on success, +or a negative value on error. +@end deftypefun + +@subheading gnutls_x509_privkey_export +@anchor{gnutls_x509_privkey_export} +@deftypefun {int} {gnutls_x509_privkey_export} (gnutls_x509_privkey_t @var{key}, gnutls_x509_crt_fmt_t @var{format}, void * @var{output_data}, size_t * @var{output_data_size}) +@var{key}: Holds the key + +@var{format}: the format of output params. One of PEM or DER. + +@var{output_data}: will contain a private key PEM or DER encoded + +@var{output_data_size}: holds the size of output_data (and will be +replaced by the actual size of parameters) + +This function will export the private key to a PKCS1 structure for +RSA keys, or an integer sequence for DSA keys. The DSA keys are in +the same format with the parameters used by openssl. + +If the buffer provided is not long enough to hold the output, then +*output_data_size is updated and GNUTLS_E_SHORT_MEMORY_BUFFER will +be returned. + +If the structure is PEM encoded, it will have a header +of "BEGIN RSA PRIVATE KEY". + +@strong{Return value:} In case of failure a negative value will be +returned, and 0 on success. +@end deftypefun + +@subheading gnutls_x509_privkey_export_rsa_raw +@anchor{gnutls_x509_privkey_export_rsa_raw} +@deftypefun {int} {gnutls_x509_privkey_export_rsa_raw} (gnutls_x509_privkey_t @var{key}, gnutls_datum_t * @var{m}, gnutls_datum_t * @var{e}, gnutls_datum_t * @var{d}, gnutls_datum_t * @var{p}, gnutls_datum_t * @var{q}, gnutls_datum_t * @var{u}) +@var{key}: a structure that holds the rsa parameters + +@var{m}: will hold the modulus + +@var{e}: will hold the public exponent + +@var{d}: will hold the private exponent + +@var{p}: will hold the first prime (p) + +@var{q}: will hold the second prime (q) + +@var{u}: will hold the coefficient + +This function will export the RSA private key's parameters found in the given +structure. The new parameters will be allocated using +@code{gnutls_malloc()} and will be stored in the appropriate datum. +@end deftypefun + +@subheading gnutls_x509_privkey_export_dsa_raw +@anchor{gnutls_x509_privkey_export_dsa_raw} +@deftypefun {int} {gnutls_x509_privkey_export_dsa_raw} (gnutls_x509_privkey_t @var{key}, gnutls_datum_t * @var{p}, gnutls_datum_t * @var{q}, gnutls_datum_t * @var{g}, gnutls_datum_t * @var{y}, gnutls_datum_t * @var{x}) +@var{p}: will hold the p + +@var{q}: will hold the q + +@var{g}: will hold the g + +@var{y}: will hold the y + +@var{x}: will hold the x + +This function will export the DSA private key's parameters found in the given +structure. The new parameters will be allocated using +@code{gnutls_malloc()} and will be stored in the appropriate datum. +@end deftypefun + +@subheading gnutls_x509_privkey_generate +@anchor{gnutls_x509_privkey_generate} +@deftypefun {int} {gnutls_x509_privkey_generate} (gnutls_x509_privkey_t @var{key}, gnutls_pk_algorithm_t @var{algo}, unsigned int @var{bits}, unsigned int @var{flags}) +@var{key}: should contain a gnutls_x509_privkey_t structure + +@var{algo}: is one of RSA or DSA. + +@var{bits}: the size of the modulus + +@var{flags}: unused for now. Must be 0. + +This function will generate a random private key. Note that +this function must be called on an empty private key. + +Returns 0 on success or a negative value on error. +@end deftypefun + +@subheading gnutls_x509_privkey_get_key_id +@anchor{gnutls_x509_privkey_get_key_id} +@deftypefun {int} {gnutls_x509_privkey_get_key_id} (gnutls_x509_privkey_t @var{key}, unsigned int @var{flags}, unsigned char * @var{output_data}, size_t * @var{output_data_size}) +@var{key}: Holds the key + +@var{flags}: should be 0 for now + +@var{output_data}: will contain the key ID + +@var{output_data_size}: holds the size of output_data (and will be +replaced by the actual size of parameters) + +This function will return a unique ID the depends on the public key +parameters. This ID can be used in checking whether a certificate +corresponds to the given key. + +If the buffer provided is not long enough to hold the output, then +*output_data_size is updated and GNUTLS_E_SHORT_MEMORY_BUFFER will +be returned. The output will normally be a SHA-1 hash output, +which is 20 bytes. + +@strong{Return value:} In case of failure a negative value will be +returned, and 0 on success. +@end deftypefun + +@subheading gnutls_x509_privkey_sign_data +@anchor{gnutls_x509_privkey_sign_data} +@deftypefun {int} {gnutls_x509_privkey_sign_data} (gnutls_x509_privkey_t @var{key}, gnutls_digest_algorithm_t @var{digest}, unsigned int @var{flags}, const gnutls_datum_t * @var{data}, void * @var{signature}, size_t * @var{signature_size}) +@var{key}: Holds the key + +@var{digest}: should be MD5 or SHA1 + +@var{flags}: should be 0 for now + +@var{data}: holds the data to be signed + +@var{signature}: will contain the signature + +@var{signature_size}: holds the size of signature (and will be replaced +by the new size) + +This function will sign the given data using a signature algorithm +supported by the private key. Signature algorithms are always used +together with a hash functions. Different hash functions may be +used for the RSA algorithm, but only SHA-1 for the DSA keys. + +If the buffer provided is not long enough to hold the output, then +*signature_size is updated and GNUTLS_E_SHORT_MEMORY_BUFFER will +be returned. + +In case of failure a negative value will be returned, and +0 on success. +@end deftypefun + +@subheading gnutls_x509_privkey_sign_hash +@anchor{gnutls_x509_privkey_sign_hash} +@deftypefun {int} {gnutls_x509_privkey_sign_hash} (gnutls_x509_privkey_t @var{key}, const gnutls_datum_t * @var{hash}, gnutls_datum_t * @var{signature}) +@var{key}: Holds the key + +@var{hash}: holds the data to be signed + +@var{signature}: will contain newly allocated signature + +This function will sign the given hash using the private key. + +@strong{Return value:} In case of failure a negative value will be returned, +and 0 on success. +@end deftypefun + +@subheading gnutls_x509_privkey_verify_data +@anchor{gnutls_x509_privkey_verify_data} +@deftypefun {int} {gnutls_x509_privkey_verify_data} (gnutls_x509_privkey_t @var{key}, unsigned int @var{flags}, const gnutls_datum_t * @var{data}, const gnutls_datum_t * @var{signature}) +@var{key}: Holds the key + +@var{flags}: should be 0 for now + +@var{data}: holds the data to be signed + +@var{signature}: contains the signature + +This function will verify the given signed data, using the parameters in the +private key. + +In case of a verification failure 0 is returned, and +1 on success. +@end deftypefun + +@subheading gnutls_x509_privkey_fix +@anchor{gnutls_x509_privkey_fix} +@deftypefun {int} {gnutls_x509_privkey_fix} (gnutls_x509_privkey_t @var{key}) +@var{key}: Holds the key + +This function will recalculate the secondary parameters in a key. +In RSA keys, this can be the coefficient and exponent1,2. + +@strong{Return value:} In case of failure a negative value will be +returned, and 0 on success. +@end deftypefun + +@subheading gnutls_pkcs7_init +@anchor{gnutls_pkcs7_init} +@deftypefun {int} {gnutls_pkcs7_init} (gnutls_pkcs7_t * @var{pkcs7}) +@var{pkcs7}: The structure to be initialized + +This function will initialize a PKCS7 structure. PKCS7 structures +usually contain lists of X.509 Certificates and X.509 Certificate +revocation lists. + +Returns 0 on success. +@end deftypefun + +@subheading gnutls_pkcs7_deinit +@anchor{gnutls_pkcs7_deinit} +@deftypefun {void} {gnutls_pkcs7_deinit} (gnutls_pkcs7_t @var{pkcs7}) +@var{pkcs7}: The structure to be initialized + +This function will deinitialize a PKCS7 structure. +@end deftypefun + +@subheading gnutls_pkcs7_import +@anchor{gnutls_pkcs7_import} +@deftypefun {int} {gnutls_pkcs7_import} (gnutls_pkcs7_t @var{pkcs7}, const gnutls_datum_t * @var{data}, gnutls_x509_crt_fmt_t @var{format}) +@var{pkcs7}: The structure to store the parsed PKCS7. + +@var{data}: The DER or PEM encoded PKCS7. + +@var{format}: One of DER or PEM + +This function will convert the given DER or PEM encoded PKCS7 +to the native gnutls_pkcs7_t format. The output will be stored in 'pkcs7'. + +If the PKCS7 is PEM encoded it should have a header of "PKCS7". + +Returns 0 on success. +@end deftypefun + +@subheading gnutls_pkcs7_get_crt_raw +@anchor{gnutls_pkcs7_get_crt_raw} +@deftypefun {int} {gnutls_pkcs7_get_crt_raw} (gnutls_pkcs7_t @var{pkcs7}, int @var{indx}, void * @var{certificate}, size_t * @var{certificate_size}) +@var{indx}: contains the index of the certificate to extract + +@var{certificate}: the contents of the certificate will be copied there (may be null) + +@var{certificate_size}: should hold the size of the certificate + +This function will return a certificate of the PKCS7 or RFC2630 certificate set. +Returns 0 on success. If the provided buffer is not long enough, +then @code{certificate_size} is updated and GNUTLS_E_SHORT_MEMORY_BUFFER is returned. + +After the last certificate has been read GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE +will be returned. +@end deftypefun + +@subheading gnutls_pkcs7_get_crt_count +@anchor{gnutls_pkcs7_get_crt_count} +@deftypefun {int} {gnutls_pkcs7_get_crt_count} (gnutls_pkcs7_t @var{pkcs7}) +This function will return the number of certifcates in the PKCS7 or +RFC2630 certificate set. + +Returns a negative value on failure. +@end deftypefun + +@subheading gnutls_pkcs7_export +@anchor{gnutls_pkcs7_export} +@deftypefun {int} {gnutls_pkcs7_export} (gnutls_pkcs7_t @var{pkcs7}, gnutls_x509_crt_fmt_t @var{format}, void * @var{output_data}, size_t * @var{output_data_size}) +@var{pkcs7}: Holds the pkcs7 structure + +@var{format}: the format of output params. One of PEM or DER. + +@var{output_data}: will contain a structure PEM or DER encoded + +@var{output_data_size}: holds the size of output_data (and will be +replaced by the actual size of parameters) + +This function will export the pkcs7 structure to DER or PEM format. + +If the buffer provided is not long enough to hold the output, then +*output_data_size is updated and GNUTLS_E_SHORT_MEMORY_BUFFER will +be returned. + +If the structure is PEM encoded, it will have a header +of "BEGIN PKCS7". + +@strong{Return value:} In case of failure a negative value will be +returned, and 0 on success. +@end deftypefun + +@subheading gnutls_pkcs7_set_crt_raw +@anchor{gnutls_pkcs7_set_crt_raw} +@deftypefun {int} {gnutls_pkcs7_set_crt_raw} (gnutls_pkcs7_t @var{pkcs7}, const gnutls_datum_t * @var{crt}) +@var{crt}: the DER encoded certificate to be added + +This function will add a certificate to the PKCS7 or RFC2630 certificate set. +Returns 0 on success. +@end deftypefun + +@subheading gnutls_pkcs7_set_crt +@anchor{gnutls_pkcs7_set_crt} +@deftypefun {int} {gnutls_pkcs7_set_crt} (gnutls_pkcs7_t @var{pkcs7}, gnutls_x509_crt_t @var{crt}) +@var{crt}: the certificate to be copied. + +This function will add a parsed certificate to the PKCS7 or RFC2630 certificate set. +This is a wrapper function over @code{gnutls_pkcs7_set_crt_raw()} . + +Returns 0 on success. +@end deftypefun + +@subheading gnutls_pkcs7_delete_crt +@anchor{gnutls_pkcs7_delete_crt} +@deftypefun {int} {gnutls_pkcs7_delete_crt} (gnutls_pkcs7_t @var{pkcs7}, int @var{indx}) +@var{indx}: the index of the certificate to delete + +This function will delete a certificate from a PKCS7 or RFC2630 certificate set. +Index starts from 0. Returns 0 on success. +@end deftypefun + +@subheading gnutls_pkcs7_get_crl_raw +@anchor{gnutls_pkcs7_get_crl_raw} +@deftypefun {int} {gnutls_pkcs7_get_crl_raw} (gnutls_pkcs7_t @var{pkcs7}, int @var{indx}, void * @var{crl}, size_t * @var{crl_size}) +@var{indx}: contains the index of the crl to extract + +@var{crl}: the contents of the crl will be copied there (may be null) + +@var{crl_size}: should hold the size of the crl + +This function will return a crl of the PKCS7 or RFC2630 crl set. +Returns 0 on success. If the provided buffer is not long enough, +then @code{crl_size} is updated and GNUTLS_E_SHORT_MEMORY_BUFFER is returned. + +After the last crl has been read GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE +will be returned. +@end deftypefun + +@subheading gnutls_pkcs7_get_crl_count +@anchor{gnutls_pkcs7_get_crl_count} +@deftypefun {int} {gnutls_pkcs7_get_crl_count} (gnutls_pkcs7_t @var{pkcs7}) +This function will return the number of certifcates in the PKCS7 or +RFC2630 crl set. + +Returns a negative value on failure. +@end deftypefun + +@subheading gnutls_pkcs7_set_crl_raw +@anchor{gnutls_pkcs7_set_crl_raw} +@deftypefun {int} {gnutls_pkcs7_set_crl_raw} (gnutls_pkcs7_t @var{pkcs7}, const gnutls_datum_t * @var{crl}) +@var{crl}: the DER encoded crl to be added + +This function will add a crl to the PKCS7 or RFC2630 crl set. +Returns 0 on success. +@end deftypefun + +@subheading gnutls_pkcs7_set_crl +@anchor{gnutls_pkcs7_set_crl} +@deftypefun {int} {gnutls_pkcs7_set_crl} (gnutls_pkcs7_t @var{pkcs7}, gnutls_x509_crl_t @var{crl}) +@var{crl}: the DER encoded crl to be added + +This function will add a parsed crl to the PKCS7 or RFC2630 crl set. +Returns 0 on success. +@end deftypefun + +@subheading gnutls_pkcs7_delete_crl +@anchor{gnutls_pkcs7_delete_crl} +@deftypefun {int} {gnutls_pkcs7_delete_crl} (gnutls_pkcs7_t @var{pkcs7}, int @var{indx}) +@var{indx}: the index of the crl to delete + +This function will delete a crl from a PKCS7 or RFC2630 crl set. +Index starts from 0. Returns 0 on success. +@end deftypefun + +@subheading gnutls_x509_crq_init +@anchor{gnutls_x509_crq_init} +@deftypefun {int} {gnutls_x509_crq_init} (gnutls_x509_crq_t * @var{crq}) +@var{crq}: The structure to be initialized + +This function will initialize a PKCS10 certificate request structure. + +Returns 0 on success. +@end deftypefun + +@subheading gnutls_x509_crq_deinit +@anchor{gnutls_x509_crq_deinit} +@deftypefun {void} {gnutls_x509_crq_deinit} (gnutls_x509_crq_t @var{crq}) +@var{crq}: The structure to be initialized + +This function will deinitialize a CRL structure. +@end deftypefun + +@subheading gnutls_x509_crq_import +@anchor{gnutls_x509_crq_import} +@deftypefun {int} {gnutls_x509_crq_import} (gnutls_x509_crq_t @var{crq}, const gnutls_datum_t * @var{data}, gnutls_x509_crt_fmt_t @var{format}) +@var{crq}: The structure to store the parsed certificate request. + +@var{data}: The DER or PEM encoded certificate. + +@var{format}: One of DER or PEM + +This function will convert the given DER or PEM encoded Certificate +to the native gnutls_x509_crq_t format. The output will be stored in @code{cert}. + +If the Certificate is PEM encoded it should have a header of "NEW CERTIFICATE REQUEST". + +Returns 0 on success. +@end deftypefun + +@subheading gnutls_x509_crq_get_dn +@anchor{gnutls_x509_crq_get_dn} +@deftypefun {int} {gnutls_x509_crq_get_dn} (gnutls_x509_crq_t @var{crq}, char * @var{buf}, size_t * @var{sizeof_buf}) +@var{crq}: should contain a gnutls_x509_crq_t structure + +@var{buf}: a pointer to a structure to hold the name (may be null) + +@var{sizeof_buf}: initially holds the size of @code{buf} + +This function will copy the name of the Certificate request +subject in the provided buffer. The name will be in the form +"C=xxxx,O=yyyy,CN=zzzz" as described in RFC2253. The output string +will be ASCII or UTF-8 encoded, depending on the certificate data. + +If @code{buf} is null then only the size will be filled. + +Returns GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is not +long enough, and in that case the *sizeof_buf will be updated with +the required size. On success 0 is returned. +@end deftypefun + +@subheading gnutls_x509_crq_get_dn_by_oid +@anchor{gnutls_x509_crq_get_dn_by_oid} +@deftypefun {int} {gnutls_x509_crq_get_dn_by_oid} (gnutls_x509_crq_t @var{crq}, const char * @var{oid}, int @var{indx}, unsigned int @var{raw_flag}, void * @var{buf}, size_t * @var{sizeof_buf}) +@var{crq}: should contain a gnutls_x509_crq_t structure + +@var{oid}: holds an Object Identified in null terminated string + +@var{indx}: In case multiple same OIDs exist in the RDN, this specifies +which to send. Use zero to get the first one. + +@var{raw_flag}: If non zero returns the raw DER data of the DN part. + +@var{buf}: a pointer to a structure to hold the name (may be null) + +@var{sizeof_buf}: initially holds the size of @code{buf} + +This function will extract the part of the name of the Certificate +request subject, specified by the given OID. The output will be +encoded as described in RFC2253. The output string will be ASCII +or UTF-8 encoded, depending on the certificate data. + +Some helper macros with popular OIDs can be found in gnutls/x509.h +If raw flag is zero, this function will only return known OIDs as +text. Other OIDs will be DER encoded, as described in RFC2253 -- +in hex format with a '\#' prefix. You can check about known OIDs +using @code{gnutls_x509_dn_oid_known()}. + +If @code{buf} is null then only the size will be filled. + +Returns GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is not +long enough, and in that case the *sizeof_buf will be updated with +the required size. On success 0 is returned. +@end deftypefun + +@subheading gnutls_x509_crq_get_dn_oid +@anchor{gnutls_x509_crq_get_dn_oid} +@deftypefun {int} {gnutls_x509_crq_get_dn_oid} (gnutls_x509_crq_t @var{crq}, int @var{indx}, void * @var{oid}, size_t * @var{sizeof_oid}) +@var{crq}: should contain a gnutls_x509_crq_t structure + +@var{indx}: Specifies which DN OID to send. Use zero to get the first one. + +@var{oid}: a pointer to a structure to hold the name (may be null) + +@var{sizeof_oid}: initially holds the size of @code{oid} + +This function will extract the requested OID of the name of the +Certificate request subject, specified by the given index. + +If oid is null then only the size will be filled. + +Returns GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is not +long enough, and in that case the *sizeof_oid will be updated with +the required size. On success 0 is returned. +@end deftypefun + +@subheading gnutls_x509_crq_get_challenge_password +@anchor{gnutls_x509_crq_get_challenge_password} +@deftypefun {int} {gnutls_x509_crq_get_challenge_password} (gnutls_x509_crq_t @var{crq}, char * @var{pass}, size_t * @var{sizeof_pass}) +@var{crq}: should contain a gnutls_x509_crq_t structure + +@var{pass}: will hold a null terminated password + +@var{sizeof_pass}: Initially holds the size of @code{pass}. + +This function will return the challenge password in the +request. + +Returns 0 on success. +@end deftypefun + +@subheading gnutls_x509_crq_set_attribute_by_oid +@anchor{gnutls_x509_crq_set_attribute_by_oid} +@deftypefun {int} {gnutls_x509_crq_set_attribute_by_oid} (gnutls_x509_crq_t @var{crq}, const char * @var{oid}, void * @var{buf}, size_t @var{sizeof_buf}) +@var{crq}: should contain a gnutls_x509_crq_t structure + +@var{oid}: holds an Object Identified in null terminated string + +@var{buf}: a pointer to a structure that holds the attribute data + +@var{sizeof_buf}: holds the size of @code{buf} + +This function will set the attribute in the certificate request specified +by the given Object ID. The attribute must be be DER encoded. + +Returns 0 on success. +@end deftypefun + +@subheading gnutls_x509_crq_get_attribute_by_oid +@anchor{gnutls_x509_crq_get_attribute_by_oid} +@deftypefun {int} {gnutls_x509_crq_get_attribute_by_oid} (gnutls_x509_crq_t @var{crq}, const char * @var{oid}, int @var{indx}, void * @var{buf}, size_t * @var{sizeof_buf}) +@var{crq}: should contain a gnutls_x509_crq_t structure + +@var{oid}: holds an Object Identified in null terminated string + +@var{indx}: In case multiple same OIDs exist in the attribute list, this specifies +which to send. Use zero to get the first one. + +@var{buf}: a pointer to a structure to hold the attribute data (may be null) + +@var{sizeof_buf}: initially holds the size of @code{buf} + +This function will return the attribute in the certificate request specified +by the given Object ID. The attribute will be DER encoded. + +Returns 0 on success. +@end deftypefun + +@subheading gnutls_x509_crq_set_dn_by_oid +@anchor{gnutls_x509_crq_set_dn_by_oid} +@deftypefun {int} {gnutls_x509_crq_set_dn_by_oid} (gnutls_x509_crq_t @var{crq}, const char * @var{oid}, unsigned int @var{raw_flag}, const void * @var{data}, unsigned int @var{sizeof_data}) +@var{crq}: should contain a gnutls_x509_crq_t structure + +@var{oid}: holds an Object Identifier in a null terminated string + +@var{raw_flag}: must be 0, or 1 if the data are DER encoded + +@var{data}: a pointer to the input data + +@var{sizeof_data}: holds the size of @code{data} + +This function will set the part of the name of the Certificate request subject, specified +by the given OID. The input string should be ASCII or UTF-8 encoded. + +Some helper macros with popular OIDs can be found in gnutls/x509.h +With this function you can only set the known OIDs. You can test +for known OIDs using @code{gnutls_x509_dn_oid_known()}. For OIDs that are +not known (by gnutls) you should properly DER encode your data, and +call this function with raw_flag set. + +Returns 0 on success. +@end deftypefun + +@subheading gnutls_x509_crq_set_version +@anchor{gnutls_x509_crq_set_version} +@deftypefun {int} {gnutls_x509_crq_set_version} (gnutls_x509_crq_t @var{crq}, unsigned int @var{version}) +@var{crq}: should contain a gnutls_x509_crq_t structure + +@var{version}: holds the version number. For v1 Requests must be 1. + +This function will set the version of the certificate request. For +version 1 requests this must be one. + +Returns 0 on success. +@end deftypefun + +@subheading gnutls_x509_crq_get_version +@anchor{gnutls_x509_crq_get_version} +@deftypefun {int} {gnutls_x509_crq_get_version} (gnutls_x509_crq_t @var{crq}) +@var{crq}: should contain a gnutls_x509_crq_t structure + +This function will return the version of the specified Certificate request. + +Returns a negative value on error. +@end deftypefun + +@subheading gnutls_x509_crq_set_key +@anchor{gnutls_x509_crq_set_key} +@deftypefun {int} {gnutls_x509_crq_set_key} (gnutls_x509_crq_t @var{crq}, gnutls_x509_privkey_t @var{key}) +@var{crq}: should contain a gnutls_x509_crq_t structure + +@var{key}: holds a private key + +This function will set the public parameters from the given private key to the +request. Only RSA keys are currently supported. + +Returns 0 on success. +@end deftypefun + +@subheading gnutls_x509_crq_set_challenge_password +@anchor{gnutls_x509_crq_set_challenge_password} +@deftypefun {int} {gnutls_x509_crq_set_challenge_password} (gnutls_x509_crq_t @var{crq}, const char * @var{pass}) +@var{crq}: should contain a gnutls_x509_crq_t structure + +@var{pass}: holds a null terminated password + +This function will set a challenge password to be used when revoking the request. + +Returns 0 on success. +@end deftypefun + +@subheading gnutls_x509_crq_sign2 +@anchor{gnutls_x509_crq_sign2} +@deftypefun {int} {gnutls_x509_crq_sign2} (gnutls_x509_crq_t @var{crq}, gnutls_x509_privkey_t @var{key}, gnutls_digest_algorithm_t @var{dig}, unsigned int @var{flags}) +@var{crq}: should contain a gnutls_x509_crq_t structure + +@var{key}: holds a private key + +@var{dig}: The message digest to use. GNUTLS_DIG_SHA1 is the safe choice unless you know what you're doing. + +@var{flags}: must be 0 + +This function will sign the certificate request with a private key. +This must be the same key as the one used in @code{gnutls_x509_crt_set_key()} since a +certificate request is self signed. + +This must be the last step in a certificate request generation since all +the previously set parameters are now signed. + +Returns 0 on success. +@end deftypefun + +@subheading gnutls_x509_crq_sign +@anchor{gnutls_x509_crq_sign} +@deftypefun {int} {gnutls_x509_crq_sign} (gnutls_x509_crq_t @var{crq}, gnutls_x509_privkey_t @var{key}) +@var{crq}: should contain a gnutls_x509_crq_t structure + +@var{key}: holds a private key + +This function is the same a @code{gnutls_x509_crq_sign2()} with no flags, and +SHA1 as the hash algorithm. + +Returns 0 on success. +@end deftypefun + +@subheading gnutls_x509_crq_export +@anchor{gnutls_x509_crq_export} +@deftypefun {int} {gnutls_x509_crq_export} (gnutls_x509_crq_t @var{crq}, gnutls_x509_crt_fmt_t @var{format}, void * @var{output_data}, size_t * @var{output_data_size}) +@var{crq}: Holds the request + +@var{format}: the format of output params. One of PEM or DER. + +@var{output_data}: will contain a certificate request PEM or DER encoded + +@var{output_data_size}: holds the size of output_data (and will be +replaced by the actual size of parameters) + +This function will export the certificate request to a PKCS10 + +If the buffer provided is not long enough to hold the output, then +GNUTLS_E_SHORT_MEMORY_BUFFER will be returned and +*output_data_size will be updated. + +If the structure is PEM encoded, it will have a header of "BEGIN +NEW CERTIFICATE REQUEST". + +@strong{Return value:} In case of failure a negative value will be +returned, and 0 on success. +@end deftypefun + +@subheading gnutls_x509_crq_get_pk_algorithm +@anchor{gnutls_x509_crq_get_pk_algorithm} +@deftypefun {int} {gnutls_x509_crq_get_pk_algorithm} (gnutls_x509_crq_t @var{crq}, unsigned int * @var{bits}) +@var{crq}: should contain a gnutls_x509_crq_t structure + +@var{bits}: if bits is non null it will hold the size of the parameters' in bits + +This function will return the public key algorithm of a PKCS \@code{10} +certificate request. + +If bits is non null, it should have enough size to hold the parameters +size in bits. For RSA the bits returned is the modulus. +For DSA the bits returned are of the public +exponent. + +Returns a member of the gnutls_pk_algorithm_t enumeration on success, +or a negative value on error. +@end deftypefun + +@subheading gnutls_x509_privkey_export_pkcs8 +@anchor{gnutls_x509_privkey_export_pkcs8} +@deftypefun {int} {gnutls_x509_privkey_export_pkcs8} (gnutls_x509_privkey_t @var{key}, gnutls_x509_crt_fmt_t @var{format}, const char * @var{password}, unsigned int @var{flags}, void * @var{output_data}, size_t * @var{output_data_size}) +@var{key}: Holds the key + +@var{format}: the format of output params. One of PEM or DER. + +@var{password}: the password that will be used to encrypt the key. + +@var{flags}: an ORed sequence of gnutls_pkcs_encrypt_flags_t + +@var{output_data}: will contain a private key PEM or DER encoded + +@var{output_data_size}: holds the size of output_data (and will be +replaced by the actual size of parameters) + +This function will export the private key to a PKCS8 structure. +Both RSA and DSA keys can be exported. For DSA keys we use +PKCS @code{11} definitions. If the flags do not specify the encryption +cipher, then the default 3DES (PBES2) will be used. + +The @code{password} can be either ASCII or UTF-8 in the default PBES2 +encryption schemas, or ASCII for the PKCS12 schemas. + +If the buffer provided is not long enough to hold the output, then +*output_data_size is updated and GNUTLS_E_SHORT_MEMORY_BUFFER will +be returned. + +If the structure is PEM encoded, it will have a header +of "BEGIN ENCRYPTED PRIVATE KEY" or "BEGIN PRIVATE KEY" if +encryption is not used. + +@strong{Return value:} In case of failure a negative value will be +returned, and 0 on success. +@end deftypefun + +@subheading gnutls_x509_privkey_import_pkcs8 +@anchor{gnutls_x509_privkey_import_pkcs8} +@deftypefun {int} {gnutls_x509_privkey_import_pkcs8} (gnutls_x509_privkey_t @var{key}, const gnutls_datum_t * @var{data}, gnutls_x509_crt_fmt_t @var{format}, const char * @var{password}, unsigned int @var{flags}) +@var{key}: The structure to store the parsed key + +@var{data}: The DER or PEM encoded key. + +@var{format}: One of DER or PEM + +@var{password}: the password to decrypt the key (if it is encrypted). + +@var{flags}: 0 if encrypted or GNUTLS_PKCS_PLAIN if not encrypted. + +This function will convert the given DER or PEM encoded PKCS8 2.0 encrypted key +to the native gnutls_x509_privkey_t format. The output will be stored in @code{key}. +Both RSA and DSA keys can be imported, and flags can only be used to indicate +an unencrypted key. + +The @code{password} can be either ASCII or UTF-8 in the default PBES2 +encryption schemas, or ASCII for the PKCS12 schemas. + +If the Certificate is PEM encoded it should have a header of "ENCRYPTED PRIVATE KEY", +or "PRIVATE KEY". You only need to specify the flags if the key is DER encoded, since +in that case the encryption status cannot be auto-detected. + +Returns 0 on success. +@end deftypefun + +@subheading gnutls_pkcs12_init +@anchor{gnutls_pkcs12_init} +@deftypefun {int} {gnutls_pkcs12_init} (gnutls_pkcs12_t * @var{pkcs12}) +@var{pkcs12}: The structure to be initialized + +This function will initialize a PKCS12 structure. PKCS12 structures +usually contain lists of X.509 Certificates and X.509 Certificate +revocation lists. + +Returns 0 on success. +@end deftypefun + +@subheading gnutls_pkcs12_deinit +@anchor{gnutls_pkcs12_deinit} +@deftypefun {void} {gnutls_pkcs12_deinit} (gnutls_pkcs12_t @var{pkcs12}) +@var{pkcs12}: The structure to be initialized + +This function will deinitialize a PKCS12 structure. +@end deftypefun + +@subheading gnutls_pkcs12_import +@anchor{gnutls_pkcs12_import} +@deftypefun {int} {gnutls_pkcs12_import} (gnutls_pkcs12_t @var{pkcs12}, const gnutls_datum_t * @var{data}, gnutls_x509_crt_fmt_t @var{format}, unsigned int @var{flags}) +@var{pkcs12}: The structure to store the parsed PKCS12. + +@var{data}: The DER or PEM encoded PKCS12. + +@var{format}: One of DER or PEM + +@var{flags}: an ORed sequence of gnutls_privkey_pkcs8_flags + +This function will convert the given DER or PEM encoded PKCS12 +to the native gnutls_pkcs12_t format. The output will be stored in 'pkcs12'. + +If the PKCS12 is PEM encoded it should have a header of "PKCS12". + +Returns 0 on success. +@end deftypefun + +@subheading gnutls_pkcs12_export +@anchor{gnutls_pkcs12_export} +@deftypefun {int} {gnutls_pkcs12_export} (gnutls_pkcs12_t @var{pkcs12}, gnutls_x509_crt_fmt_t @var{format}, void * @var{output_data}, size_t * @var{output_data_size}) +@var{pkcs12}: Holds the pkcs12 structure + +@var{format}: the format of output params. One of PEM or DER. + +@var{output_data}: will contain a structure PEM or DER encoded + +@var{output_data_size}: holds the size of output_data (and will be +replaced by the actual size of parameters) + +This function will export the pkcs12 structure to DER or PEM format. + +If the buffer provided is not long enough to hold the output, then +*output_data_size will be updated and GNUTLS_E_SHORT_MEMORY_BUFFER +will be returned. + +If the structure is PEM encoded, it will have a header +of "BEGIN PKCS12". + +@strong{Return value:} In case of failure a negative value will be +returned, and 0 on success. +@end deftypefun + +@subheading gnutls_pkcs12_get_bag +@anchor{gnutls_pkcs12_get_bag} +@deftypefun {int} {gnutls_pkcs12_get_bag} (gnutls_pkcs12_t @var{pkcs12}, int @var{indx}, gnutls_pkcs12_bag_t @var{bag}) +@var{pkcs12}: should contain a gnutls_pkcs12_t structure + +@var{indx}: contains the index of the bag to extract + +@var{bag}: An initialized bag, where the contents of the bag will be copied + +This function will return a Bag from the PKCS12 structure. +Returns 0 on success. + +After the last Bag has been read GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE +will be returned. +@end deftypefun + +@subheading gnutls_pkcs12_set_bag +@anchor{gnutls_pkcs12_set_bag} +@deftypefun {int} {gnutls_pkcs12_set_bag} (gnutls_pkcs12_t @var{pkcs12}, gnutls_pkcs12_bag_t @var{bag}) +@var{pkcs12}: should contain a gnutls_pkcs12_t structure + +@var{bag}: An initialized bag + +This function will insert a Bag into the PKCS12 structure. +Returns 0 on success. +@end deftypefun + +@subheading gnutls_pkcs12_generate_mac +@anchor{gnutls_pkcs12_generate_mac} +@deftypefun {int} {gnutls_pkcs12_generate_mac} (gnutls_pkcs12_t @var{pkcs12}, const char * @var{pass}) +@var{pkcs12}: should contain a gnutls_pkcs12_t structure + +@var{pass}: The password for the MAC + +This function will generate a MAC for the PKCS12 structure. +Returns 0 on success. +@end deftypefun + +@subheading gnutls_pkcs12_verify_mac +@anchor{gnutls_pkcs12_verify_mac} +@deftypefun {int} {gnutls_pkcs12_verify_mac} (gnutls_pkcs12_t @var{pkcs12}, const char * @var{pass}) +@var{pkcs12}: should contain a gnutls_pkcs12_t structure + +@var{pass}: The password for the MAC + +This function will verify the MAC for the PKCS12 structure. +Returns 0 on success. +@end deftypefun + +@subheading gnutls_pkcs12_bag_init +@anchor{gnutls_pkcs12_bag_init} +@deftypefun {int} {gnutls_pkcs12_bag_init} (gnutls_pkcs12_bag_t * @var{bag}) +@var{bag}: The structure to be initialized + +This function will initialize a PKCS12 bag structure. PKCS12 Bags +usually contain private keys, lists of X.509 Certificates and X.509 Certificate +revocation lists. + +Returns 0 on success. +@end deftypefun + +@subheading gnutls_pkcs12_bag_deinit +@anchor{gnutls_pkcs12_bag_deinit} +@deftypefun {void} {gnutls_pkcs12_bag_deinit} (gnutls_pkcs12_bag_t @var{bag}) +@var{bag}: The structure to be initialized + +This function will deinitialize a PKCS12 Bag structure. +@end deftypefun + +@subheading gnutls_pkcs12_bag_get_type +@anchor{gnutls_pkcs12_bag_get_type} +@deftypefun {gnutls_pkcs12_bag_type_t} {gnutls_pkcs12_bag_get_type} (gnutls_pkcs12_bag_t @var{bag}, int @var{indx}) +@var{bag}: The bag + +@var{indx}: The element of the bag to get the type + +This function will return the bag's type. One of the gnutls_pkcs12_bag_type_t +enumerations. +@end deftypefun + +@subheading gnutls_pkcs12_bag_get_count +@anchor{gnutls_pkcs12_bag_get_count} +@deftypefun {int} {gnutls_pkcs12_bag_get_count} (gnutls_pkcs12_bag_t @var{bag}) +@var{bag}: The bag + +This function will return the number of the elements withing the bag. +@end deftypefun + +@subheading gnutls_pkcs12_bag_get_data +@anchor{gnutls_pkcs12_bag_get_data} +@deftypefun {int} {gnutls_pkcs12_bag_get_data} (gnutls_pkcs12_bag_t @var{bag}, int @var{indx}, gnutls_datum_t * @var{data}) +@var{bag}: The bag + +@var{indx}: The element of the bag to get the data from + +@var{data}: where the bag's data will be. Should be treated as constant. + +This function will return the bag's data. The data is a constant +that is stored into the bag. Should not be accessed after the bag +is deleted. + +Returns 0 on success and a negative error code on error. +@end deftypefun + +@subheading gnutls_pkcs12_bag_set_data +@anchor{gnutls_pkcs12_bag_set_data} +@deftypefun {int} {gnutls_pkcs12_bag_set_data} (gnutls_pkcs12_bag_t @var{bag}, gnutls_pkcs12_bag_type_t @var{type}, const gnutls_datum_t * @var{data}) +@var{bag}: The bag + +@var{type}: The data's type + +@var{data}: the data to be copied. + +This function will insert the given data of the given type into the +bag. + +Returns the index of the added bag on success, or a negative +value on error. +@end deftypefun + +@subheading gnutls_pkcs12_bag_set_crt +@anchor{gnutls_pkcs12_bag_set_crt} +@deftypefun {int} {gnutls_pkcs12_bag_set_crt} (gnutls_pkcs12_bag_t @var{bag}, gnutls_x509_crt_t @var{crt}) +@var{bag}: The bag + +@var{crt}: the certificate to be copied. + +This function will insert the given certificate into the +bag. This is just a wrapper over @code{gnutls_pkcs12_bag_set_data()}. + +Returns the index of the added bag on success, or a negative +value on failure. +@end deftypefun + +@subheading gnutls_pkcs12_bag_set_crl +@anchor{gnutls_pkcs12_bag_set_crl} +@deftypefun {int} {gnutls_pkcs12_bag_set_crl} (gnutls_pkcs12_bag_t @var{bag}, gnutls_x509_crl_t @var{crl}) +@var{bag}: The bag + +@var{crl}: the CRL to be copied. + +This function will insert the given CRL into the +bag. This is just a wrapper over @code{gnutls_pkcs12_bag_set_data()}. + +Returns the index of the added bag on success, or a negative +value on failure. +@end deftypefun + +@subheading gnutls_pkcs12_bag_set_key_id +@anchor{gnutls_pkcs12_bag_set_key_id} +@deftypefun {int} {gnutls_pkcs12_bag_set_key_id} (gnutls_pkcs12_bag_t @var{bag}, int @var{indx}, const gnutls_datum_t * @var{id}) +@var{bag}: The bag + +@var{indx}: The bag's element to add the id + +@var{id}: the ID + +This function will add the given key ID, to the specified, by the index, bag +element. The key ID will be encoded as a 'Local key identifier' bag attribute, +which is usually used to distinguish the local private key and the certificate pair. + +Returns 0 on success, or a negative value on error. +@end deftypefun + +@subheading gnutls_pkcs12_bag_get_key_id +@anchor{gnutls_pkcs12_bag_get_key_id} +@deftypefun {int} {gnutls_pkcs12_bag_get_key_id} (gnutls_pkcs12_bag_t @var{bag}, int @var{indx}, gnutls_datum_t * @var{id}) +@var{bag}: The bag + +@var{indx}: The bag's element to add the id + +@var{id}: where the ID will be copied (to be treated as const) + +This function will return the key ID, of the specified bag element. +The key ID is usually used to distinguish the local private key and the certificate pair. + +Returns 0 on success, or a negative value on error. +@end deftypefun + +@subheading gnutls_pkcs12_bag_get_friendly_name +@anchor{gnutls_pkcs12_bag_get_friendly_name} +@deftypefun {int} {gnutls_pkcs12_bag_get_friendly_name} (gnutls_pkcs12_bag_t @var{bag}, int @var{indx}, char ** @var{name}) +@var{bag}: The bag + +@var{indx}: The bag's element to add the id + +@var{name}: will hold a pointer to the name (to be treated as const) + +This function will return the friendly name, of the specified bag element. +The key ID is usually used to distinguish the local private key and the certificate pair. + +Returns 0 on success, or a negative value on error. +@end deftypefun + +@subheading gnutls_pkcs12_bag_set_friendly_name +@anchor{gnutls_pkcs12_bag_set_friendly_name} +@deftypefun {int} {gnutls_pkcs12_bag_set_friendly_name} (gnutls_pkcs12_bag_t @var{bag}, int @var{indx}, const char * @var{name}) +@var{bag}: The bag + +@var{indx}: The bag's element to add the id + +@var{name}: the name + +This function will add the given key friendly name, to the specified, by the index, bag +element. The name will be encoded as a 'Friendly name' bag attribute, +which is usually used to set a user name to the local private key and the certificate pair. + +Returns 0 on success, or a negative value on error. +@end deftypefun + +@subheading gnutls_pkcs12_bag_decrypt +@anchor{gnutls_pkcs12_bag_decrypt} +@deftypefun {int} {gnutls_pkcs12_bag_decrypt} (gnutls_pkcs12_bag_t @var{bag}, const char * @var{pass}) +@var{bag}: The bag + +@var{pass}: The password used for encryption. This can only be ASCII. + +This function will decrypt the given encrypted bag and return 0 on success. +@end deftypefun + +@subheading gnutls_pkcs12_bag_encrypt +@anchor{gnutls_pkcs12_bag_encrypt} +@deftypefun {int} {gnutls_pkcs12_bag_encrypt} (gnutls_pkcs12_bag_t @var{bag}, const char * @var{pass}, unsigned int @var{flags}) +@var{bag}: The bag + +@var{pass}: The password used for encryption. This can only be ASCII. + +@var{flags}: should be one of gnutls_pkcs_encrypt_flags_t elements bitwise or'd + +This function will encrypt the given bag and return 0 on success. +@end deftypefun + +@subheading gnutls_x509_crt_set_dn_by_oid +@anchor{gnutls_x509_crt_set_dn_by_oid} +@deftypefun {int} {gnutls_x509_crt_set_dn_by_oid} (gnutls_x509_crt_t @var{crt}, const char * @var{oid}, unsigned int @var{raw_flag}, const void * @var{name}, unsigned int @var{sizeof_name}) +@var{crt}: should contain a gnutls_x509_crt_t structure + +@var{oid}: holds an Object Identifier in a null terminated string + +@var{raw_flag}: must be 0, or 1 if the data are DER encoded + +@var{name}: a pointer to the name + +@var{sizeof_name}: holds the size of @code{name} + +This function will set the part of the name of the Certificate subject, specified +by the given OID. The input string should be ASCII or UTF-8 encoded. + +Some helper macros with popular OIDs can be found in gnutls/x509.h +With this function you can only set the known OIDs. You can test +for known OIDs using @code{gnutls_x509_dn_oid_known()}. For OIDs that are +not known (by gnutls) you should properly DER encode your data, and +call this function with raw_flag set. + +Returns 0 on success. +@end deftypefun + +@subheading gnutls_x509_crt_set_issuer_dn_by_oid +@anchor{gnutls_x509_crt_set_issuer_dn_by_oid} +@deftypefun {int} {gnutls_x509_crt_set_issuer_dn_by_oid} (gnutls_x509_crt_t @var{crt}, const char * @var{oid}, unsigned int @var{raw_flag}, const void * @var{name}, unsigned int @var{sizeof_name}) +@var{crt}: should contain a gnutls_x509_crt_t structure + +@var{oid}: holds an Object Identifier in a null terminated string + +@var{raw_flag}: must be 0, or 1 if the data are DER encoded + +@var{name}: a pointer to the name + +@var{sizeof_name}: holds the size of @code{name} + +This function will set the part of the name of the Certificate issuer, specified +by the given OID. The input string should be ASCII or UTF-8 encoded. + +Some helper macros with popular OIDs can be found in gnutls/x509.h +With this function you can only set the known OIDs. You can test +for known OIDs using @code{gnutls_x509_dn_oid_known()}. For OIDs that are +not known (by gnutls) you should properly DER encode your data, and +call this function with raw_flag set. + +Normally you do not need to call this function, since the signing +operation will copy the signer's name as the issuer of the certificate. + +Returns 0 on success. +@end deftypefun + +@subheading gnutls_x509_crt_set_proxy_dn +@anchor{gnutls_x509_crt_set_proxy_dn} +@deftypefun {int} {gnutls_x509_crt_set_proxy_dn} (gnutls_x509_crt_t @var{crt}, gnutls_x509_crt_t @var{eecrt}, unsigned int @var{raw_flag}, const void * @var{name}, unsigned int @var{sizeof_name}) +@var{crt}: a gnutls_x509_crt_t structure with the new proxy cert + +@var{eecrt}: the end entity certificate that will be issuing the proxy + +@var{raw_flag}: must be 0, or 1 if the CN is DER encoded + +@var{name}: a pointer to the CN name, may be NULL (but MUST then be added later) + +@var{sizeof_name}: holds the size of @code{name} + +This function will set the subject in @code{crt} to the end entity's +@code{eecrt} subject name, and add a single Common Name component @code{name} +of size @code{sizeof_name}. This corresponds to the required proxy +certificate naming style. Note that if @code{name} is @code{NULL}, you MUST +set it later by using @code{gnutls_x509_crt_set_dn_by_oid()} or similar. + +Returns 0 on success. +@end deftypefun + +@subheading gnutls_x509_crt_set_version +@anchor{gnutls_x509_crt_set_version} +@deftypefun {int} {gnutls_x509_crt_set_version} (gnutls_x509_crt_t @var{crt}, unsigned int @var{version}) +@var{crt}: should contain a gnutls_x509_crt_t structure + +@var{version}: holds the version number. For X.509v1 certificates must be 1. + +This function will set the version of the certificate. This must +be one for X.509 version 1, and so on. Plain certificates without +extensions must have version set to one. + +To create well-formed certificates, you must specify version 3 if +you use any certificate extensions. Extensions are created by +functions such as gnutls_x509_crt_set_subject_alternative_name or +gnutls_x509_crt_set_key_usage. + +Returns 0 on success. +@end deftypefun + +@subheading gnutls_x509_crt_set_key +@anchor{gnutls_x509_crt_set_key} +@deftypefun {int} {gnutls_x509_crt_set_key} (gnutls_x509_crt_t @var{crt}, gnutls_x509_privkey_t @var{key}) +@var{crt}: should contain a gnutls_x509_crt_t structure + +@var{key}: holds a private key + +This function will set the public parameters from the given private key to the +certificate. Only RSA keys are currently supported. + +Returns 0 on success. +@end deftypefun + +@subheading gnutls_x509_crt_set_crq +@anchor{gnutls_x509_crt_set_crq} +@deftypefun {int} {gnutls_x509_crt_set_crq} (gnutls_x509_crt_t @var{crt}, gnutls_x509_crq_t @var{crq}) +@var{crt}: should contain a gnutls_x509_crt_t structure + +@var{crq}: holds a certificate request + +This function will set the name and public parameters from the given certificate request to the +certificate. Only RSA keys are currently supported. + +Returns 0 on success. +@end deftypefun + +@subheading gnutls_x509_crt_set_extension_by_oid +@anchor{gnutls_x509_crt_set_extension_by_oid} +@deftypefun {int} {gnutls_x509_crt_set_extension_by_oid} (gnutls_x509_crt_t @var{crt}, const char * @var{oid}, const void * @var{buf}, size_t @var{sizeof_buf}, unsigned int @var{critical}) +@var{crt}: should contain a gnutls_x509_crt_t structure + +@var{oid}: holds an Object Identified in null terminated string + +@var{buf}: a pointer to a DER encoded data + +@var{sizeof_buf}: holds the size of @code{buf} + +@var{critical}: should be non zero if the extension is to be marked as critical + +This function will set an the extension, by the specified OID, in the certificate. +The extension data should be binary data DER encoded. + +Returns 0 on success and a negative value in case of an error. +@end deftypefun + +@subheading gnutls_x509_crt_set_basic_constraints +@anchor{gnutls_x509_crt_set_basic_constraints} +@deftypefun {int} {gnutls_x509_crt_set_basic_constraints} (gnutls_x509_crt_t @var{crt}, unsigned int @var{ca}, int @var{pathLenConstraint}) +@var{crt}: should contain a gnutls_x509_crt_t structure + +@var{ca}: true(1) or false(0). Depending on the Certificate authority status. + +@var{pathLenConstraint}: non-negative values indicate maximum length of path, +and negative values indicate that the pathLenConstraints field should +not be present. + +This function will set the basicConstraints certificate extension. + +Returns 0 on success. +@end deftypefun + +@subheading gnutls_x509_crt_set_ca_status +@anchor{gnutls_x509_crt_set_ca_status} +@deftypefun {int} {gnutls_x509_crt_set_ca_status} (gnutls_x509_crt_t @var{crt}, unsigned int @var{ca}) +@var{crt}: should contain a gnutls_x509_crt_t structure + +@var{ca}: true(1) or false(0). Depending on the Certificate authority status. + +This function will set the basicConstraints certificate extension. +Use @code{gnutls_x509_crt_set_basic_constraints()} if you want to control +the pathLenConstraint field too. + +Returns 0 on success. +@end deftypefun + +@subheading gnutls_x509_crt_set_key_usage +@anchor{gnutls_x509_crt_set_key_usage} +@deftypefun {int} {gnutls_x509_crt_set_key_usage} (gnutls_x509_crt_t @var{crt}, unsigned int @var{usage}) +@var{crt}: should contain a gnutls_x509_crt_t structure + +@var{usage}: an ORed sequence of the GNUTLS_KEY_* elements. + +This function will set the keyUsage certificate extension. + +Returns 0 on success. +@end deftypefun + +@subheading gnutls_x509_crt_set_subject_alternative_name +@anchor{gnutls_x509_crt_set_subject_alternative_name} +@deftypefun {int} {gnutls_x509_crt_set_subject_alternative_name} (gnutls_x509_crt_t @var{crt}, gnutls_x509_subject_alt_name_t @var{type}, const char * @var{data_string}) +@var{crt}: should contain a gnutls_x509_crt_t structure + +@var{type}: is one of the gnutls_x509_subject_alt_name_t enumerations + +@var{data_string}: The data to be set + +This function will set the subject alternative name certificate extension. + +Returns 0 on success. +@end deftypefun + +@subheading gnutls_x509_crt_set_proxy +@anchor{gnutls_x509_crt_set_proxy} +@deftypefun {int} {gnutls_x509_crt_set_proxy} (gnutls_x509_crt_t @var{crt}, int @var{pathLenConstraint}, const char * @var{policyLanguage}, const char * @var{policy}, size_t @var{sizeof_policy}) +@var{crt}: should contain a gnutls_x509_crt_t structure + +@var{pathLenConstraint}: non-negative values indicate maximum length of path, +and negative values indicate that the pathLenConstraints field should +not be present. + +@var{policyLanguage}: OID describing the language of @code{policy}. + +@var{policy}: opaque byte array with policy language, can be @code{NULL} + +@var{sizeof_policy}: size of @code{policy}. + +This function will set the proxyCertInfo extension. + +Returns 0 on success. +@end deftypefun + +@subheading gnutls_x509_crt_sign2 +@anchor{gnutls_x509_crt_sign2} +@deftypefun {int} {gnutls_x509_crt_sign2} (gnutls_x509_crt_t @var{crt}, gnutls_x509_crt_t @var{issuer}, gnutls_x509_privkey_t @var{issuer_key}, gnutls_digest_algorithm_t @var{dig}, unsigned int @var{flags}) +@var{crt}: should contain a gnutls_x509_crt_t structure + +@var{issuer}: is the certificate of the certificate issuer + +@var{issuer_key}: holds the issuer's private key + +@var{dig}: The message digest to use. GNUTLS_DIG_SHA1 is the safe choice unless you know what you're doing. + +@var{flags}: must be 0 + +This function will sign the certificate with the issuer's private key, and +will copy the issuer's information into the certificate. + +This must be the last step in a certificate generation since all +the previously set parameters are now signed. + +Returns 0 on success. +@end deftypefun + +@subheading gnutls_x509_crt_sign +@anchor{gnutls_x509_crt_sign} +@deftypefun {int} {gnutls_x509_crt_sign} (gnutls_x509_crt_t @var{crt}, gnutls_x509_crt_t @var{issuer}, gnutls_x509_privkey_t @var{issuer_key}) +@var{crt}: should contain a gnutls_x509_crt_t structure + +@var{issuer}: is the certificate of the certificate issuer + +@var{issuer_key}: holds the issuer's private key + +This function is the same a @code{gnutls_x509_crt_sign2()} with no flags, and +SHA1 as the hash algorithm. + +Returns 0 on success. +@end deftypefun + +@subheading gnutls_x509_crt_set_activation_time +@anchor{gnutls_x509_crt_set_activation_time} +@deftypefun {int} {gnutls_x509_crt_set_activation_time} (gnutls_x509_crt_t @var{cert}, time_t @var{act_time}) +@var{cert}: should contain a gnutls_x509_crt_t structure + +@var{act_time}: The actual time + +This function will set the time this Certificate was or will be activated. + +Returns 0 on success, or a negative value in case of an error. +@end deftypefun + +@subheading gnutls_x509_crt_set_expiration_time +@anchor{gnutls_x509_crt_set_expiration_time} +@deftypefun {int} {gnutls_x509_crt_set_expiration_time} (gnutls_x509_crt_t @var{cert}, time_t @var{exp_time}) +@var{cert}: should contain a gnutls_x509_crt_t structure + +@var{exp_time}: The actual time + +This function will set the time this Certificate will expire. + +Returns 0 on success, or a negative value in case of an error. +@end deftypefun + +@subheading gnutls_x509_crt_set_serial +@anchor{gnutls_x509_crt_set_serial} +@deftypefun {int} {gnutls_x509_crt_set_serial} (gnutls_x509_crt_t @var{cert}, const void * @var{serial}, size_t @var{serial_size}) +@var{cert}: should contain a gnutls_x509_crt_t structure + +@var{serial}: The serial number + +@var{serial_size}: Holds the size of the serial field. + +This function will set the X.509 certificate's serial number. +Serial is not always a 32 or 64bit number. Some CAs use +large serial numbers, thus it may be wise to handle it as something +opaque. + +Returns 0 on success, or a negative value in case of an error. +@end deftypefun + +@subheading gnutls_x509_crt_set_crl_dist_points +@anchor{gnutls_x509_crt_set_crl_dist_points} +@deftypefun {int} {gnutls_x509_crt_set_crl_dist_points} (gnutls_x509_crt_t @var{crt}, gnutls_x509_subject_alt_name_t @var{type}, const void * @var{data_string}, unsigned int @var{reason_flags}) +@var{crt}: should contain a gnutls_x509_crt_t structure + +@var{type}: is one of the gnutls_x509_subject_alt_name_t enumerations + +@var{data_string}: The data to be set + +@var{reason_flags}: revocation reasons + +This function will set the CRL distribution points certificate extension. + +Returns 0 on success. +@end deftypefun + +@subheading gnutls_x509_crt_cpy_crl_dist_points +@anchor{gnutls_x509_crt_cpy_crl_dist_points} +@deftypefun {int} {gnutls_x509_crt_cpy_crl_dist_points} (gnutls_x509_crt_t @var{dst}, gnutls_x509_crt_t @var{src}) +@var{dst}: should contain a gnutls_x509_crt_t structure + +@var{src}: the certificate where the dist points will be copied from + +This function will copy the CRL distribution points certificate +extension, from the source to the destination certificate. +This may be useful to copy from a CA certificate to issued ones. + +Returns 0 on success. +@end deftypefun + +@subheading gnutls_x509_crt_set_subject_key_id +@anchor{gnutls_x509_crt_set_subject_key_id} +@deftypefun {int} {gnutls_x509_crt_set_subject_key_id} (gnutls_x509_crt_t @var{cert}, const void * @var{id}, size_t @var{id_size}) +@var{cert}: should contain a gnutls_x509_crt_t structure + +@var{id}: The key ID + +@var{id_size}: Holds the size of the serial field. + +This function will set the X.509 certificate's subject key ID extension. + +Returns 0 on success, or a negative value in case of an error. +@end deftypefun + +@subheading gnutls_x509_crt_set_authority_key_id +@anchor{gnutls_x509_crt_set_authority_key_id} +@deftypefun {int} {gnutls_x509_crt_set_authority_key_id} (gnutls_x509_crt_t @var{cert}, const void * @var{id}, size_t @var{id_size}) +@var{cert}: should contain a gnutls_x509_crt_t structure + +@var{id}: The key ID + +@var{id_size}: Holds the size of the serial field. + +This function will set the X.509 certificate's authority key ID extension. +Only the keyIdentifier field can be set with this function. + +Returns 0 on success, or a negative value in case of an error. +@end deftypefun + +@subheading gnutls_x509_crt_set_key_purpose_oid +@anchor{gnutls_x509_crt_set_key_purpose_oid} +@deftypefun {int} {gnutls_x509_crt_set_key_purpose_oid} (gnutls_x509_crt_t @var{cert}, const void * @var{oid}, unsigned int @var{critical}) +@var{cert}: should contain a gnutls_x509_crt_t structure + +@var{oid}: a pointer to a null terminated string that holds the OID + +@var{critical}: Whether this extension will be critical or not + +This function will set the key purpose OIDs of the Certificate. +These are stored in the Extended Key Usage extension (2.5.29.37) +See the GNUTLS_KP_* definitions for human readable names. + +Subsequent calls to this function will append OIDs to the OID list. + +On success 0 is returned. +@end deftypefun + +@subheading gnutls_x509_crl_set_version +@anchor{gnutls_x509_crl_set_version} +@deftypefun {int} {gnutls_x509_crl_set_version} (gnutls_x509_crl_t @var{crl}, unsigned int @var{version}) +@var{crl}: should contain a gnutls_x509_crl_t structure + +@var{version}: holds the version number. For CRLv1 crls must be 1. + +This function will set the version of the CRL. This +must be one for CRL version 1, and so on. The CRLs generated +by gnutls should have a version number of 2. + +Returns 0 on success. +@end deftypefun + +@subheading gnutls_x509_crl_sign2 +@anchor{gnutls_x509_crl_sign2} +@deftypefun {int} {gnutls_x509_crl_sign2} (gnutls_x509_crl_t @var{crl}, gnutls_x509_crt_t @var{issuer}, gnutls_x509_privkey_t @var{issuer_key}, gnutls_digest_algorithm_t @var{dig}, unsigned int @var{flags}) +@var{crl}: should contain a gnutls_x509_crl_t structure + +@var{issuer}: is the certificate of the certificate issuer + +@var{issuer_key}: holds the issuer's private key + +@var{dig}: The message digest to use. GNUTLS_DIG_SHA1 is the safe choice unless you know what you're doing. + +@var{flags}: must be 0 + +This function will sign the CRL with the issuer's private key, and +will copy the issuer's information into the CRL. + +This must be the last step in a certificate CRL since all +the previously set parameters are now signed. + +Returns 0 on success. +@end deftypefun + +@subheading gnutls_x509_crl_sign +@anchor{gnutls_x509_crl_sign} +@deftypefun {int} {gnutls_x509_crl_sign} (gnutls_x509_crl_t @var{crl}, gnutls_x509_crt_t @var{issuer}, gnutls_x509_privkey_t @var{issuer_key}) +@var{crl}: should contain a gnutls_x509_crl_t structure + +@var{issuer}: is the certificate of the certificate issuer + +@var{issuer_key}: holds the issuer's private key + +This function is the same a @code{gnutls_x509_crl_sign2()} with no flags, and +SHA1 as the hash algorithm. + +Returns 0 on success. +@end deftypefun + +@subheading gnutls_x509_crl_set_this_update +@anchor{gnutls_x509_crl_set_this_update} +@deftypefun {int} {gnutls_x509_crl_set_this_update} (gnutls_x509_crl_t @var{crl}, time_t @var{act_time}) +@var{crl}: should contain a gnutls_x509_crl_t structure + +@var{act_time}: The actual time + +This function will set the time this CRL was issued. + +Returns 0 on success, or a negative value in case of an error. +@end deftypefun + +@subheading gnutls_x509_crl_set_next_update +@anchor{gnutls_x509_crl_set_next_update} +@deftypefun {int} {gnutls_x509_crl_set_next_update} (gnutls_x509_crl_t @var{crl}, time_t @var{exp_time}) +@var{crl}: should contain a gnutls_x509_crl_t structure + +@var{exp_time}: The actual time + +This function will set the time this CRL will be updated. + +Returns 0 on success, or a negative value in case of an error. +@end deftypefun + +@subheading gnutls_x509_crl_set_crt_serial +@anchor{gnutls_x509_crl_set_crt_serial} +@deftypefun {int} {gnutls_x509_crl_set_crt_serial} (gnutls_x509_crl_t @var{crl}, const void * @var{serial}, size_t @var{serial_size}, time_t @var{revocation_time}) +@var{crl}: should contain a gnutls_x509_crl_t structure + +@var{serial}: The revoked certificate's serial number + +@var{serial_size}: Holds the size of the serial field. + +@var{revocation_time}: The time this certificate was revoked + +This function will set a revoked certificate's serial number to the CRL. + +Returns 0 on success, or a negative value in case of an error. +@end deftypefun + +@subheading gnutls_x509_crl_set_crt +@anchor{gnutls_x509_crl_set_crt} +@deftypefun {int} {gnutls_x509_crl_set_crt} (gnutls_x509_crl_t @var{crl}, gnutls_x509_crt_t @var{crt}, time_t @var{revocation_time}) +@var{crl}: should contain a gnutls_x509_crl_t structure + +@var{crt}: should contain a gnutls_x509_crt_t structure with the revoked certificate + +@var{revocation_time}: The time this certificate was revoked + +This function will set a revoked certificate's serial number to the CRL. + +Returns 0 on success, or a negative value in case of an error. +@end deftypefun + +@subheading gnutls_x509_crt_print +@anchor{gnutls_x509_crt_print} +@deftypefun {int} {gnutls_x509_crt_print} (gnutls_x509_crt_t @var{cert}, gnutls_certificate_print_formats_t @var{format}, gnutls_datum_t * @var{out}) +@var{cert}: The structure to be printed + +@var{format}: Indicate the format to use + +@var{out}: Newly allocated datum with zero terminated string. + +This function will pretty print a X.509 certificate, suitable for +display to a human. + +If the format is @code{GNUTLS_X509_CRT_FULL} then all fields of the +certificate will be output, on multiple lines. The +@code{GNUTLS_X509_CRT_ONELINE} format will generate one line with some +selected fields, which is useful for logging purposes. + +The output @code{out} needs to be deallocate using @code{gnutls_free()}. + +Returns 0 on success. +@end deftypefun + +@subheading gnutls_x509_crl_print +@anchor{gnutls_x509_crl_print} +@deftypefun {int} {gnutls_x509_crl_print} (gnutls_x509_crl_t @var{crl}, gnutls_certificate_print_formats_t @var{format}, gnutls_datum_t * @var{out}) +@var{crl}: The structure to be printed + +@var{format}: Indicate the format to use + +@var{out}: Newly allocated datum with zero terminated string. + +This function will pretty print a X.509 certificate revocation +list, suitable for display to a human. + +The output @code{out} needs to be deallocate using @code{gnutls_free()}. + +Returns 0 on success. +@end deftypefun + diff --git a/src/daemon/https/x509/x509.c b/src/daemon/https/x509/x509.c new file mode 100644 index 00000000..96999d27 --- /dev/null +++ b/src/daemon/https/x509/x509.c @@ -0,0 +1,2851 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2006, 2007 Free Software Foundation + * Author: Nikos Mavrogiannopoulos, Simon Josefsson, Howard Chu + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +/* Functions on X.509 Certificate parsing + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * gnutls_x509_crt_init - This function initializes a gnutls_x509_crt_t structure + * @cert: The structure to be initialized + * + * This function will initialize an X.509 certificate structure. + * + * Returns 0 on success. + * + **/ +int +gnutls_x509_crt_init (gnutls_x509_crt_t * cert) +{ + gnutls_x509_crt_t tmp = gnutls_calloc (1, sizeof (gnutls_x509_crt_int)); + int result; + + if (!tmp) + return GNUTLS_E_MEMORY_ERROR; + + result = asn1_create_element (_gnutls_get_pkix (), + "PKIX1.Certificate", &tmp->cert); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + gnutls_free (tmp); + return _gnutls_asn2err (result); + } + + *cert = tmp; + + return 0; /* success */ +} + +/*- + * _gnutls_x509_crt_cpy - This function copies a gnutls_x509_crt_t structure + * @dest: The structure where to copy + * @src: The structure to be copied + * + * This function will copy an X.509 certificate structure. + * + * Returns 0 on success. + * + -*/ +int +_gnutls_x509_crt_cpy (gnutls_x509_crt_t dest, gnutls_x509_crt_t src) +{ + int ret; + size_t der_size; + opaque *der; + gnutls_datum_t tmp; + + ret = gnutls_x509_crt_export (src, GNUTLS_X509_FMT_DER, NULL, &der_size); + if (ret != GNUTLS_E_SHORT_MEMORY_BUFFER) + { + gnutls_assert (); + return ret; + } + + der = gnutls_alloca (der_size); + if (der == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + ret = gnutls_x509_crt_export (src, GNUTLS_X509_FMT_DER, der, &der_size); + if (ret < 0) + { + gnutls_assert (); + gnutls_afree (der); + return ret; + } + + tmp.data = der; + tmp.size = der_size; + ret = gnutls_x509_crt_import (dest, &tmp, GNUTLS_X509_FMT_DER); + + gnutls_afree (der); + + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + return 0; + +} + +/** + * gnutls_x509_crt_deinit - This function deinitializes memory used by a gnutls_x509_crt_t structure + * @cert: The structure to be initialized + * + * This function will deinitialize a CRL structure. + * + **/ +void +gnutls_x509_crt_deinit (gnutls_x509_crt_t cert) +{ + if (!cert) + return; + + if (cert->cert) + asn1_delete_structure (&cert->cert); + + gnutls_free (cert); +} + +/** + * gnutls_x509_crt_import - This function will import a DER or PEM encoded Certificate + * @cert: The structure to store the parsed certificate. + * @data: The DER or PEM encoded certificate. + * @format: One of DER or PEM + * + * This function will convert the given DER or PEM encoded Certificate + * to the native gnutls_x509_crt_t format. The output will be stored in @cert. + * + * If the Certificate is PEM encoded it should have a header of "X509 CERTIFICATE", or + * "CERTIFICATE". + * + * Returns 0 on success. + * + **/ +int +gnutls_x509_crt_import (gnutls_x509_crt_t cert, + const gnutls_datum_t * data, + gnutls_x509_crt_fmt_t format) +{ + int result = 0, need_free = 0; + gnutls_datum_t _data; + opaque *signature = NULL; + + if (cert == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + _data.data = data->data; + _data.size = data->size; + + /* If the Certificate is in PEM format then decode it + */ + if (format == GNUTLS_X509_FMT_PEM) + { + opaque *out; + + /* Try the first header */ + result = _gnutls_fbase64_decode (PEM_X509_CERT2, data->data, data->size, + &out); + + if (result <= 0) + { + /* try for the second header */ + result = _gnutls_fbase64_decode (PEM_X509_CERT, data->data, + data->size, &out); + + if (result <= 0) + { + if (result == 0) + result = GNUTLS_E_INTERNAL_ERROR; + gnutls_assert (); + return result; + } + } + + _data.data = out; + _data.size = result; + + need_free = 1; + } + + result = asn1_der_decoding (&cert->cert, _data.data, _data.size, NULL); + if (result != ASN1_SUCCESS) + { + result = _gnutls_asn2err (result); + gnutls_assert (); + goto cleanup; + } + + /* Since we do not want to disable any extension + */ + cert->use_extensions = 1; + if (need_free) + _gnutls_free_datum (&_data); + + return 0; + +cleanup:gnutls_free (signature); + if (need_free) + _gnutls_free_datum (&_data); + return result; +} + +/** + * gnutls_x509_crt_get_issuer_dn - This function returns the Certificate's issuer distinguished name + * @cert: should contain a gnutls_x509_crt_t structure + * @buf: a pointer to a structure to hold the name (may be null) + * @sizeof_buf: initially holds the size of @buf + * + * This function will copy the name of the Certificate issuer in the + * provided buffer. The name will be in the form + * "C=xxxx,O=yyyy,CN=zzzz" as described in RFC2253. The output string + * will be ASCII or UTF-8 encoded, depending on the certificate data. + * + * If @buf is null then only the size will be filled. + * + * Returns GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is not + * long enough, and in that case the *sizeof_buf will be updated with + * the required size. On success 0 is returned. + * + **/ +int +gnutls_x509_crt_get_issuer_dn (gnutls_x509_crt_t cert, + char *buf, size_t * sizeof_buf) +{ + if (cert == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_parse_dn (cert->cert, + "tbsCertificate.issuer.rdnSequence", buf, + sizeof_buf); +} + +/** + * gnutls_x509_crt_get_issuer_dn_by_oid - This function returns the Certificate's issuer distinguished name + * @cert: should contain a gnutls_x509_crt_t structure + * @oid: holds an Object Identified in null terminated string + * @indx: In case multiple same OIDs exist in the RDN, this specifies which to send. Use zero to get the first one. + * @raw_flag: If non zero returns the raw DER data of the DN part. + * @buf: a pointer to a structure to hold the name (may be null) + * @sizeof_buf: initially holds the size of @buf + * + * This function will extract the part of the name of the Certificate + * issuer specified by the given OID. The output, if the raw flag is not + * used, will be encoded as described in RFC2253. Thus a string that is + * ASCII or UTF-8 encoded, depending on the certificate data. + * + * Some helper macros with popular OIDs can be found in gnutls/x509.h + * If raw flag is zero, this function will only return known OIDs as + * text. Other OIDs will be DER encoded, as described in RFC2253 -- + * in hex format with a '\#' prefix. You can check about known OIDs + * using gnutls_x509_dn_oid_known(). + * + * If @buf is null then only the size will be filled. + * + * Returns GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is not + * long enough, and in that case the *sizeof_buf will be updated with + * the required size. On success 0 is returned. + * + **/ +int +gnutls_x509_crt_get_issuer_dn_by_oid (gnutls_x509_crt_t cert, + const char *oid, + int indx, + unsigned int raw_flag, + void *buf, size_t * sizeof_buf) +{ + if (cert == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_parse_dn_oid (cert->cert, + "tbsCertificate.issuer.rdnSequence", oid, + indx, raw_flag, buf, sizeof_buf); +} + +/** + * gnutls_x509_crt_get_issuer_dn_oid - This function returns the Certificate's issuer distinguished name OIDs + * @cert: should contain a gnutls_x509_crt_t structure + * @indx: This specifies which OID to return. Use zero to get the first one. + * @oid: a pointer to a buffer to hold the OID (may be null) + * @sizeof_oid: initially holds the size of @oid + * + * This function will extract the OIDs of the name of the Certificate + * issuer specified by the given index. + * + * If @oid is null then only the size will be filled. + * + * Returns GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is not + * long enough, and in that case the *sizeof_oid will be updated with + * the required size. On success 0 is returned. + * + **/ +int +gnutls_x509_crt_get_issuer_dn_oid (gnutls_x509_crt_t cert, + int indx, void *oid, size_t * sizeof_oid) +{ + if (cert == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_get_dn_oid (cert->cert, + "tbsCertificate.issuer.rdnSequence", indx, + oid, sizeof_oid); +} + +/** + * gnutls_x509_crt_get_dn - This function returns the Certificate's distinguished name + * @cert: should contain a gnutls_x509_crt_t structure + * @buf: a pointer to a structure to hold the name (may be null) + * @sizeof_buf: initially holds the size of @buf + * + * This function will copy the name of the Certificate in the + * provided buffer. The name will be in the form + * "C=xxxx,O=yyyy,CN=zzzz" as described in RFC2253. The output string + * will be ASCII or UTF-8 encoded, depending on the certificate data. + * + * If @buf is null then only the size will be filled. + * + * Returns GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is not + * long enough, and in that case the *sizeof_buf will be updated with + * the required size. On success 0 is returned. + * + **/ +int +gnutls_x509_crt_get_dn (gnutls_x509_crt_t cert, + char *buf, size_t * sizeof_buf) +{ + if (cert == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_parse_dn (cert->cert, + "tbsCertificate.subject.rdnSequence", buf, + sizeof_buf); +} + +/** + * gnutls_x509_crt_get_dn_by_oid - This function returns the Certificate's distinguished name + * @cert: should contain a gnutls_x509_crt_t structure + * @oid: holds an Object Identified in null terminated string + * @indx: In case multiple same OIDs exist in the RDN, this specifies which to send. Use zero to get the first one. + * @raw_flag: If non zero returns the raw DER data of the DN part. + * @buf: a pointer where the DN part will be copied (may be null). + * @sizeof_buf: initially holds the size of @buf + * + * This function will extract the part of the name of the Certificate + * subject specified by the given OID. The output, if the raw flag is not + * used, will be encoded as described in RFC2253. Thus a string that is + * ASCII or UTF-8 encoded, depending on the certificate data. + * + * Some helper macros with popular OIDs can be found in gnutls/x509.h + * If raw flag is zero, this function will only return known OIDs as + * text. Other OIDs will be DER encoded, as described in RFC2253 -- + * in hex format with a '\#' prefix. You can check about known OIDs + * using gnutls_x509_dn_oid_known(). + * + * If @buf is null then only the size will be filled. + * + * Returns GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is not + * long enough, and in that case the *sizeof_buf will be updated with + * the required size. On success 0 is returned. + * + **/ +int +gnutls_x509_crt_get_dn_by_oid (gnutls_x509_crt_t cert, + const char *oid, + int indx, + unsigned int raw_flag, + void *buf, size_t * sizeof_buf) +{ + if (cert == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_parse_dn_oid (cert->cert, + "tbsCertificate.subject.rdnSequence", oid, + indx, raw_flag, buf, sizeof_buf); +} + +/** + * gnutls_x509_crt_get_dn_oid - This function returns the Certificate's subject distinguished name OIDs + * @cert: should contain a gnutls_x509_crt_t structure + * @indx: This specifies which OID to return. Use zero to get the first one. + * @oid: a pointer to a buffer to hold the OID (may be null) + * @sizeof_oid: initially holds the size of @oid + * + * This function will extract the OIDs of the name of the Certificate + * subject specified by the given index. + * + * If oid is null then only the size will be filled. + * + * Returns GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is not + * long enough, and in that case the *sizeof_oid will be updated with + * the required size. On success 0 is returned. + * + **/ +int +gnutls_x509_crt_get_dn_oid (gnutls_x509_crt_t cert, + int indx, void *oid, size_t * sizeof_oid) +{ + if (cert == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_get_dn_oid (cert->cert, + "tbsCertificate.subject.rdnSequence", indx, + oid, sizeof_oid); +} + +/** + * gnutls_x509_crt_get_signature_algorithm - This function returns the Certificate's signature algorithm + * @cert: should contain a gnutls_x509_crt_t structure + * + * This function will return a value of the gnutls_sign_algorithm_t enumeration that + * is the signature algorithm. + * + * Returns a negative value on error. + * + **/ +int +gnutls_x509_crt_get_signature_algorithm (gnutls_x509_crt_t cert) +{ + int result; + gnutls_datum_t sa; + + if (cert == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + /* Read the signature algorithm. Note that parameters are not + * read. They will be read from the issuer's certificate if needed. + */ + result = + _gnutls_x509_read_value (cert->cert, "signatureAlgorithm.algorithm", &sa, + 0); + + if (result < 0) + { + gnutls_assert (); + return result; + } + + result = _gnutls_x509_oid2sign_algorithm (sa.data); + + _gnutls_free_datum (&sa); + + return result; +} + +/** + * gnutls_x509_crt_get_signature - Returns the Certificate's signature + * @cert: should contain a gnutls_x509_crt_t structure + * @sig: a pointer where the signature part will be copied (may be null). + * @sizeof_sig: initially holds the size of @sig + * + * This function will extract the signature field of a certificate. + * + * Returns 0 on success, and a negative value on error. + **/ +int +gnutls_x509_crt_get_signature (gnutls_x509_crt_t cert, + char *sig, size_t * sizeof_sig) +{ + int result; + int bits, len; + + if (cert == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + bits = 0; + result = asn1_read_value (cert->cert, "signature", NULL, &bits); + if (result != ASN1_MEM_ERROR) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + if (bits % 8 != 0) + { + gnutls_assert (); + return GNUTLS_E_CERTIFICATE_ERROR; + } + + len = bits / 8; + + if (*sizeof_sig < len) + { + *sizeof_sig = bits / 8; + return GNUTLS_E_SHORT_MEMORY_BUFFER; + } + + result = asn1_read_value (cert->cert, "signature", sig, &len); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + return 0; +} + +/** + * gnutls_x509_crt_get_version - This function returns the Certificate's version number + * @cert: should contain a gnutls_x509_crt_t structure + * + * This function will return the version of the specified Certificate. + * + * Returns a negative value on error. + * + **/ +int +gnutls_x509_crt_get_version (gnutls_x509_crt_t cert) +{ + opaque version[5]; + int len, result; + + if (cert == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + len = sizeof (version); + if ((result = + asn1_read_value (cert->cert, "tbsCertificate.version", version, + &len)) != ASN1_SUCCESS) + { + + if (result == ASN1_ELEMENT_NOT_FOUND) + return 1; /* the DEFAULT version */ + gnutls_assert (); + return _gnutls_asn2err (result); + } + + return (int) version[0] + 1; +} + +/** + * gnutls_x509_crt_get_activation_time - This function returns the Certificate's activation time + * @cert: should contain a gnutls_x509_crt_t structure + * + * This function will return the time this Certificate was or will be activated. + * + * Returns (time_t)-1 on error. + * + **/ +time_t +gnutls_x509_crt_get_activation_time (gnutls_x509_crt_t cert) +{ + if (cert == NULL) + { + gnutls_assert (); + return (time_t) - 1; + } + + return _gnutls_x509_get_time (cert->cert, + "tbsCertificate.validity.notBefore"); +} + +/** + * gnutls_x509_crt_get_expiration_time - This function returns the Certificate's expiration time + * @cert: should contain a gnutls_x509_crt_t structure + * + * This function will return the time this Certificate was or will be expired. + * + * Returns (time_t)-1 on error. + * + **/ +time_t +gnutls_x509_crt_get_expiration_time (gnutls_x509_crt_t cert) +{ + if (cert == NULL) + { + gnutls_assert (); + return (time_t) - 1; + } + + return _gnutls_x509_get_time (cert->cert, + "tbsCertificate.validity.notAfter"); +} + +/** + * gnutls_x509_crt_get_serial - This function returns the certificate's serial number + * @cert: should contain a gnutls_x509_crt_t structure + * @result: The place where the serial number will be copied + * @result_size: Holds the size of the result field. + * + * This function will return the X.509 certificate's serial number. + * This is obtained by the X509 Certificate serialNumber + * field. Serial is not always a 32 or 64bit number. Some CAs use + * large serial numbers, thus it may be wise to handle it as something + * opaque. + * + * Returns 0 on success and a negative value in case of an error. + * + **/ +int +gnutls_x509_crt_get_serial (gnutls_x509_crt_t cert, + void *result, size_t * result_size) +{ + int ret, len; + + if (cert == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + len = *result_size; + ret + = + asn1_read_value (cert->cert, "tbsCertificate.serialNumber", result, &len); + *result_size = len; + + if (ret != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (ret); + } + + return 0; +} + +/** + * gnutls_x509_crt_get_subject_key_id - This function returns the certificate's key identifier + * @cert: should contain a gnutls_x509_crt_t structure + * @ret: The place where the identifier will be copied + * @ret_size: Holds the size of the result field. + * @critical: will be non zero if the extension is marked as critical (may be null) + * + * This function will return the X.509v3 certificate's subject key identifier. + * This is obtained by the X.509 Subject Key identifier extension + * field (2.5.29.14). + * + * Returns 0 on success and a negative value in case of an error. + * + **/ +int +gnutls_x509_crt_get_subject_key_id (gnutls_x509_crt_t cert, + void *ret, + size_t * ret_size, unsigned int *critical) +{ + int result, len; + gnutls_datum_t id; + ASN1_TYPE c2 = ASN1_TYPE_EMPTY; + + if (cert == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + if (ret) + memset (ret, 0, *ret_size); + else + *ret_size = 0; + + if ((result = _gnutls_x509_crt_get_extension (cert, "2.5.29.14", 0, &id, + critical)) < 0) + { + return result; + } + + if (id.size == 0 || id.data == NULL) + { + gnutls_assert (); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + result = + asn1_create_element (_gnutls_get_pkix (), "PKIX1.SubjectKeyIdentifier", + &c2); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + _gnutls_free_datum (&id); + return _gnutls_asn2err (result); + } + + result = asn1_der_decoding (&c2, id.data, id.size, NULL); + _gnutls_free_datum (&id); + + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + asn1_delete_structure (&c2); + return _gnutls_asn2err (result); + } + + len = *ret_size; + result = asn1_read_value (c2, "", ret, &len); + + *ret_size = len; + asn1_delete_structure (&c2); + + if (result == ASN1_VALUE_NOT_FOUND || result == ASN1_ELEMENT_NOT_FOUND) + { + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + return 0; +} + +/** + * gnutls_x509_crt_get_authority_key_id - This function returns the certificate authority's identifier + * @cert: should contain a gnutls_x509_crt_t structure + * @result: The place where the identifier will be copied + * @result_size: Holds the size of the result field. + * @critical: will be non zero if the extension is marked as critical (may be null) + * + * This function will return the X.509v3 certificate authority's key identifier. + * This is obtained by the X.509 Authority Key identifier extension + * field (2.5.29.35). Note that this function only returns the keyIdentifier + * field of the extension. + * + * Returns 0 on success and a negative value in case of an error. + * + **/ +int +gnutls_x509_crt_get_authority_key_id (gnutls_x509_crt_t cert, + void *ret, + size_t * ret_size, + unsigned int *critical) +{ + int result, len; + gnutls_datum_t id; + ASN1_TYPE c2 = ASN1_TYPE_EMPTY; + + if (cert == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + if (ret) + memset (ret, 0, *ret_size); + else + *ret_size = 0; + + if ((result = _gnutls_x509_crt_get_extension (cert, "2.5.29.35", 0, &id, + critical)) < 0) + { + return result; + } + + if (id.size == 0 || id.data == NULL) + { + gnutls_assert (); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + result = + asn1_create_element (_gnutls_get_pkix (), "PKIX1.AuthorityKeyIdentifier", + &c2); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + _gnutls_free_datum (&id); + return _gnutls_asn2err (result); + } + + result = asn1_der_decoding (&c2, id.data, id.size, NULL); + _gnutls_free_datum (&id); + + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + asn1_delete_structure (&c2); + return _gnutls_asn2err (result); + } + + len = *ret_size; + result = asn1_read_value (c2, "keyIdentifier", ret, &len); + + *ret_size = len; + asn1_delete_structure (&c2); + + if (result == ASN1_VALUE_NOT_FOUND || result == ASN1_ELEMENT_NOT_FOUND) + { + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + return 0; +} + +/** + * gnutls_x509_crt_get_pk_algorithm - This function returns the certificate's PublicKey algorithm + * @cert: should contain a gnutls_x509_crt_t structure + * @bits: if bits is non null it will hold the size of the parameters' in bits + * + * This function will return the public key algorithm of an X.509 + * certificate. + * + * If bits is non null, it should have enough size to hold the parameters + * size in bits. For RSA the bits returned is the modulus. + * For DSA the bits returned are of the public + * exponent. + * + * Returns a member of the gnutls_pk_algorithm_t enumeration on success, + * or a negative value on error. + * + **/ +int +gnutls_x509_crt_get_pk_algorithm (gnutls_x509_crt_t cert, unsigned int *bits) +{ + int result; + + if (cert == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + result = _gnutls_x509_get_pk_algorithm (cert->cert, + "tbsCertificate.subjectPublicKeyInfo", + bits); + + if (result < 0) + { + gnutls_assert (); + return result; + } + + return result; + +} + +inline static int +is_type_printable (int type) +{ + if (type == GNUTLS_SAN_DNSNAME || type == GNUTLS_SAN_RFC822NAME || type + == GNUTLS_SAN_URI) + return 1; + else + return 0; +} + +#define XMPP_OID "1.3.6.1.5.5.7.8.5" + +/* returns the type and the name on success. + * Type is also returned as a parameter in case of an error. + */ +static int +parse_general_name (ASN1_TYPE src, + const char *src_name, + int seq, + void *name, + size_t * name_size, + unsigned int *ret_type, int othername_oid) +{ + int len; + char nptr[MAX_NAME_SIZE]; + int result; + opaque choice_type[128]; + gnutls_x509_subject_alt_name_t type; + + seq++; /* 0->1, 1->2 etc */ + + if (src_name[0] != 0) + snprintf (nptr, sizeof (nptr), "%s.?%u", src_name, seq); + else + snprintf (nptr, sizeof (nptr), "?%u", seq); + + len = sizeof (choice_type); + result = asn1_read_value (src, nptr, choice_type, &len); + + if (result == ASN1_VALUE_NOT_FOUND || result == ASN1_ELEMENT_NOT_FOUND) + { + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + type = _gnutls_x509_san_find_type (choice_type); + if (type == (gnutls_x509_subject_alt_name_t) - 1) + { + gnutls_assert (); + return GNUTLS_E_X509_UNKNOWN_SAN; + } + + if (ret_type) + *ret_type = type; + + if (type == GNUTLS_SAN_OTHERNAME) + { + if (othername_oid) + _gnutls_str_cat (nptr, sizeof (nptr), ".otherName.type-id"); + else + _gnutls_str_cat (nptr, sizeof (nptr), ".otherName.value"); + + len = *name_size; + result = asn1_read_value (src, nptr, name, &len); + *name_size = len; + + if (result == ASN1_MEM_ERROR) + return GNUTLS_E_SHORT_MEMORY_BUFFER; + + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + if (othername_oid) + { + if (len > strlen (XMPP_OID) && strcmp (name, XMPP_OID) == 0) + type = GNUTLS_SAN_OTHERNAME_XMPP; + } + else + { + char oid[42]; + + if (src_name[0] != 0) + snprintf (nptr, sizeof (nptr), "%s.?%u.otherName.type-id", + src_name, seq); + else + snprintf (nptr, sizeof (nptr), "?%u.otherName.type-id", seq); + + len = sizeof (oid); + result = asn1_read_value (src, nptr, oid, &len); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + if (len > strlen (XMPP_OID) && strcmp (oid, XMPP_OID) == 0) + { + ASN1_TYPE c2 = ASN1_TYPE_EMPTY; + + result = + asn1_create_element (_gnutls_get_pkix (), "PKIX1.XmppAddr", + &c2); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + result = asn1_der_decoding (&c2, name, *name_size, NULL); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + asn1_delete_structure (&c2); + return _gnutls_asn2err (result); + } + + result = asn1_read_value (c2, "", name, &len); + *name_size = len; + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + asn1_delete_structure (&c2); + return _gnutls_asn2err (result); + } + asn1_delete_structure (&c2); + } + } + } + else if (type == GNUTLS_SAN_DN) + { + _gnutls_str_cat (nptr, sizeof (nptr), ".directoryName"); + result = _gnutls_x509_parse_dn (src, nptr, name, name_size); + if (result < 0) + { + gnutls_assert (); + return result; + } + } + else if (othername_oid) + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + else + { + size_t orig_name_size = *name_size; + + _gnutls_str_cat (nptr, sizeof (nptr), "."); + _gnutls_str_cat (nptr, sizeof (nptr), choice_type); + + len = *name_size; + result = asn1_read_value (src, nptr, name, &len); + *name_size = len; + + if (result == ASN1_MEM_ERROR) + { + if (is_type_printable (type)) + (*name_size)++; + return GNUTLS_E_SHORT_MEMORY_BUFFER; + } + + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + if (is_type_printable (type)) + { + + if (len + 1 > orig_name_size) + { + gnutls_assert (); + (*name_size)++; + return GNUTLS_E_SHORT_MEMORY_BUFFER; + } + + /* null terminate it */ + ((char *) name)[*name_size] = 0; + } + + } + + return type; +} + +static int +get_subject_alt_name (gnutls_x509_crt_t cert, + unsigned int seq, + void *ret, + size_t * ret_size, + unsigned int *ret_type, + unsigned int *critical, int othername_oid) +{ + int result; + gnutls_datum_t dnsname; + ASN1_TYPE c2 = ASN1_TYPE_EMPTY; + gnutls_x509_subject_alt_name_t type; + + if (cert == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + if (ret) + memset (ret, 0, *ret_size); + else + *ret_size = 0; + + if ((result = + _gnutls_x509_crt_get_extension (cert, "2.5.29.17", 0, &dnsname, + critical)) < 0) + { + return result; + } + + if (dnsname.size == 0 || dnsname.data == NULL) + { + gnutls_assert (); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + result = + asn1_create_element (_gnutls_get_pkix (), "PKIX1.SubjectAltName", &c2); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + _gnutls_free_datum (&dnsname); + return _gnutls_asn2err (result); + } + + result = asn1_der_decoding (&c2, dnsname.data, dnsname.size, NULL); + _gnutls_free_datum (&dnsname); + + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + asn1_delete_structure (&c2); + return _gnutls_asn2err (result); + } + + result = parse_general_name (c2, "", seq, ret, ret_size, ret_type, + othername_oid); + + asn1_delete_structure (&c2); + + if (result < 0) + { + return result; + } + + type = result; + + return type; +} + +/** + * gnutls_x509_crt_get_subject_alt_name - Get certificate's alternative name, if any + * @cert: should contain a gnutls_x509_crt_t structure + * @seq: specifies the sequence number of the alt name (0 for the first one, 1 for the second etc.) + * @ret: is the place where the alternative name will be copied to + * @ret_size: holds the size of ret. + * @critical: will be non zero if the extension is marked as critical (may be null) + * + * This function will return the alternative names, contained in the + * given certificate. + * + * This is specified in X509v3 Certificate Extensions. GNUTLS will + * return the Alternative name (2.5.29.17), or a negative error code. + * + * When the SAN type is otherName, it will extract the data in the + * otherName's value field, and %GNUTLS_SAN_OTHERNAME is returned. + * You may use gnutls_x509_crt_get_subject_alt_othername_oid() to get + * the corresponding OID and the "virtual" SAN types (e.g., + * %GNUTLS_SAN_OTHERNAME_XMPP). + * + * If an otherName OID is known, the data will be decoded. Otherwise + * the returned data will be DER encoded, and you will have to decode + * it yourself. Currently, only the RFC 3920 id-on-xmppAddr SAN is + * recognized. + * + * Returns the alternative subject name type on success. The type is + * one of the enumerated gnutls_x509_subject_alt_name_t. It will + * return %GNUTLS_E_SHORT_MEMORY_BUFFER if @ret_size is not large + * enough to hold the value. In that case @ret_size will be updated + * with the required size. If the certificate does not have an + * Alternative name with the specified sequence number then + * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE is returned. + * + **/ +int +gnutls_x509_crt_get_subject_alt_name (gnutls_x509_crt_t cert, + unsigned int seq, + void *ret, + size_t * ret_size, + unsigned int *critical) +{ + return get_subject_alt_name (cert, seq, ret, ret_size, NULL, critical, 0); +} + +/** + * gnutls_x509_crt_get_subject_alt_name2 - Get certificate's alternative name, if any + * @cert: should contain a gnutls_x509_crt_t structure + * @seq: specifies the sequence number of the alt name (0 for the first one, 1 for the second etc.) + * @ret: is the place where the alternative name will be copied to + * @ret_size: holds the size of ret. + * @ret_type: holds the type of the alternative name (one of gnutls_x509_subject_alt_name_t). + * @critical: will be non zero if the extension is marked as critical (may be null) + * + * This function will return the alternative names, contained in the + * given certificate. It is the same as gnutls_x509_crt_get_subject_alt_name() + * except for the fact that it will return the type of the alternative + * name in @ret_type even if the function fails for some reason (i.e. + * the buffer provided is not enough). + * + * The return values are the same as with gnutls_x509_crt_get_subject_alt_name(). + * + **/ +int +gnutls_x509_crt_get_subject_alt_name2 (gnutls_x509_crt_t cert, + unsigned int seq, + void *ret, + size_t * ret_size, + unsigned int *ret_type, + unsigned int *critical) +{ + return get_subject_alt_name (cert, seq, ret, ret_size, ret_type, critical, + 0); +} + +/** + * gnutls_x509_crt_get_subject_alt_othername_oid - Get SAN otherName OID + * @cert: should contain a gnutls_x509_crt_t structure + * @seq: specifies the sequence number of the alt name (0 for the first one, 1 for the second etc.) + * @ret: is the place where the otherName OID will be copied to + * @ret_size: holds the size of ret. + * + * This function will extract the type OID of an otherName Subject + * Alternative Name, contained in the given certificate, and return + * the type as an enumerated element. + * + * This function is only useful if + * gnutls_x509_crt_get_subject_alt_name() returned + * %GNUTLS_SAN_OTHERNAME. + * + * Returns the alternative subject name type on success. The type is + * one of the enumerated gnutls_x509_subject_alt_name_t. For + * supported OIDs, it will return one of the virtual + * (GNUTLS_SAN_OTHERNAME_*) types, e.g. %GNUTLS_SAN_OTHERNAME_XMPP, + * and %GNUTLS_SAN_OTHERNAME for unknown OIDs. It will return + * %GNUTLS_E_SHORT_MEMORY_BUFFER if @ret_size is not large enough to + * hold the value. In that case @ret_size will be updated with the + * required size. If the certificate does not have an Alternative + * name with the specified sequence number and with the otherName type + * then %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE is returned. + **/ +int +gnutls_x509_crt_get_subject_alt_othername_oid (gnutls_x509_crt_t cert, + unsigned int seq, + void *ret, size_t * ret_size) +{ + return get_subject_alt_name (cert, seq, ret, ret_size, NULL, NULL, 1); +} + +/** + * gnutls_x509_crt_get_basic_constraints - This function returns the certificate basic constraints + * @cert: should contain a gnutls_x509_crt_t structure + * @critical: will be non zero if the extension is marked as critical + * @ca: pointer to output integer indicating CA status, may be NULL, + * value is 1 if the certificate CA flag is set, 0 otherwise. + * @pathlen: pointer to output integer indicating path length (may be + * NULL), non-negative values indicate a present pathLenConstraint + * field and the actual value, -1 indicate that the field is absent. + * + * This function will read the certificate's basic constraints, and + * return the certificates CA status. It reads the basicConstraints + * X.509 extension (2.5.29.19). + * + * Return value: If the certificate is a CA a positive value will be + * returned, or zero if the certificate does not have CA flag set. A + * negative value may be returned in case of errors. If the + * certificate does not contain the basicConstraints extension + * GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be returned. + **/ +int +gnutls_x509_crt_get_basic_constraints (gnutls_x509_crt_t cert, + unsigned int *critical, + int *ca, int *pathlen) +{ + int result; + gnutls_datum_t basicConstraints; + int tmp_ca; + + if (cert == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + if ((result = _gnutls_x509_crt_get_extension (cert, "2.5.29.19", 0, + &basicConstraints, critical)) + < 0) + { + return result; + } + + if (basicConstraints.size == 0 || basicConstraints.data == NULL) + { + gnutls_assert (); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + result = _gnutls_x509_ext_extract_basicConstraints (&tmp_ca, pathlen, + basicConstraints.data, + basicConstraints.size); + if (ca) + *ca = tmp_ca; + _gnutls_free_datum (&basicConstraints); + + if (result < 0) + { + gnutls_assert (); + return result; + } + + return tmp_ca; +} + +/** + * gnutls_x509_crt_get_ca_status - This function returns the certificate CA status + * @cert: should contain a gnutls_x509_crt_t structure + * @critical: will be non zero if the extension is marked as critical + * + * This function will return certificates CA status, by reading the + * basicConstraints X.509 extension (2.5.29.19). If the certificate is + * a CA a positive value will be returned, or zero if the certificate + * does not have CA flag set. + * + * Use gnutls_x509_crt_get_basic_constraints() if you want to read the + * pathLenConstraint field too. + * + * A negative value may be returned in case of parsing error. + * If the certificate does not contain the basicConstraints extension + * GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be returned. + * + **/ +int +gnutls_x509_crt_get_ca_status (gnutls_x509_crt_t cert, unsigned int *critical) +{ + int ca, pathlen; + return gnutls_x509_crt_get_basic_constraints (cert, critical, &ca, + &pathlen); +} + +/** + * gnutls_x509_crt_get_key_usage - This function returns the certificate's key usage + * @cert: should contain a gnutls_x509_crt_t structure + * @key_usage: where the key usage bits will be stored + * @critical: will be non zero if the extension is marked as critical + * + * This function will return certificate's key usage, by reading the + * keyUsage X.509 extension (2.5.29.15). The key usage value will ORed values of the: + * GNUTLS_KEY_DIGITAL_SIGNATURE, GNUTLS_KEY_NON_REPUDIATION, + * GNUTLS_KEY_KEY_ENCIPHERMENT, GNUTLS_KEY_DATA_ENCIPHERMENT, + * GNUTLS_KEY_KEY_AGREEMENT, GNUTLS_KEY_KEY_CERT_SIGN, + * GNUTLS_KEY_CRL_SIGN, GNUTLS_KEY_ENCIPHER_ONLY, + * GNUTLS_KEY_DECIPHER_ONLY. + * + * A negative value may be returned in case of parsing error. + * If the certificate does not contain the keyUsage extension + * GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be returned. + * + **/ +int +gnutls_x509_crt_get_key_usage (gnutls_x509_crt_t cert, + unsigned int *key_usage, + unsigned int *critical) +{ + int result; + gnutls_datum_t keyUsage; + uint16_t _usage; + + if (cert == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + if ((result = + _gnutls_x509_crt_get_extension (cert, "2.5.29.15", 0, &keyUsage, + critical)) < 0) + { + return result; + } + + if (keyUsage.size == 0 || keyUsage.data == NULL) + { + gnutls_assert (); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + result = _gnutls_x509_ext_extract_keyUsage (&_usage, keyUsage.data, + keyUsage.size); + _gnutls_free_datum (&keyUsage); + + *key_usage = _usage; + + if (result < 0) + { + gnutls_assert (); + return result; + } + + return 0; +} + +/** + * gnutls_x509_crt_get_proxy - This function returns the proxy certificate info + * @cert: should contain a gnutls_x509_crt_t structure + * @critical: will be non zero if the extension is marked as critical + * @pathlen: pointer to output integer indicating path length (may be + * NULL), non-negative values indicate a present pCPathLenConstraint + * field and the actual value, -1 indicate that the field is absent. + * + * This function will read the certificate's basic constraints, and + * return the certificates CA status. It reads the basicConstraints + * X.509 extension (2.5.29.19). + * + * Return value: If the certificate is a CA a positive value will be + * returned, or zero if the certificate does not have CA flag set. A + * negative value may be returned in case of errors. If the + * certificate does not contain the basicConstraints extension + * GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be returned. + **/ +int +gnutls_x509_crt_get_proxy (gnutls_x509_crt_t cert, + unsigned int *critical, + int *pathlen, + char **policyLanguage, + char **policy, size_t * sizeof_policy) +{ + int result; + gnutls_datum_t proxyCertInfo; + + if (cert == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + if ((result = _gnutls_x509_crt_get_extension (cert, "1.3.6.1.5.5.7.1.14", 0, + &proxyCertInfo, + critical)) < 0) + { + return result; + } + + if (proxyCertInfo.size == 0 || proxyCertInfo.data == NULL) + { + gnutls_assert (); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + result = _gnutls_x509_ext_extract_proxyCertInfo (pathlen, policyLanguage, + policy, sizeof_policy, + proxyCertInfo.data, + proxyCertInfo.size); + _gnutls_free_datum (&proxyCertInfo); + if (result < 0) + { + gnutls_assert (); + return result; + } + + return 0; +} + +/** + * gnutls_x509_crt_get_extension_by_oid - This function returns the specified extension + * @cert: should contain a gnutls_x509_crt_t structure + * @oid: holds an Object Identified in null terminated string + * @indx: In case multiple same OIDs exist in the extensions, this specifies which to send. Use zero to get the first one. + * @buf: a pointer to a structure to hold the name (may be null) + * @sizeof_buf: initially holds the size of @buf + * @critical: will be non zero if the extension is marked as critical + * + * This function will return the extension specified by the OID in the certificate. + * The extensions will be returned as binary data DER encoded, in the provided + * buffer. + * + * A negative value may be returned in case of parsing error. + * If the certificate does not contain the specified extension + * GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be returned. + * + **/ +int +gnutls_x509_crt_get_extension_by_oid (gnutls_x509_crt_t cert, + const char *oid, + int indx, + void *buf, + size_t * sizeof_buf, + unsigned int *critical) +{ + int result; + gnutls_datum_t output; + + if (cert == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + if ((result = _gnutls_x509_crt_get_extension (cert, oid, indx, &output, + critical)) < 0) + { + gnutls_assert (); + return result; + } + + if (output.size == 0 || output.data == NULL) + { + gnutls_assert (); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + if (output.size > (unsigned int) *sizeof_buf) + { + *sizeof_buf = output.size; + _gnutls_free_datum (&output); + return GNUTLS_E_SHORT_MEMORY_BUFFER; + } + + *sizeof_buf = output.size; + + if (buf) + memcpy (buf, output.data, output.size); + + _gnutls_free_datum (&output); + + return 0; + +} + +/** + * gnutls_x509_crt_get_extension_oid - This function returns the specified extension OID + * @cert: should contain a gnutls_x509_crt_t structure + * @indx: Specifies which extension OID to send. Use zero to get the first one. + * @oid: a pointer to a structure to hold the OID (may be null) + * @sizeof_oid: initially holds the size of @oid + * + * This function will return the requested extension OID in the certificate. + * The extension OID will be stored as a string in the provided buffer. + * + * A negative value may be returned in case of parsing error. + * If your have reached the last extension available + * GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be returned. + * + **/ +int +gnutls_x509_crt_get_extension_oid (gnutls_x509_crt_t cert, + int indx, void *oid, size_t * sizeof_oid) +{ + int result; + + if (cert == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + result = _gnutls_x509_crt_get_extension_oid (cert, indx, oid, sizeof_oid); + if (result < 0) + { + return result; + } + + return 0; + +} + +/** + * gnutls_x509_crt_get_extension_info - Get extension id and criticality + * @cert: should contain a gnutls_x509_crt_t structure + * @indx: Specifies which extension OID to send. Use zero to get the first one. + * @oid: a pointer to a structure to hold the OID + * @sizeof_oid: initially holds the size of @oid + * @critical: output variable with critical flag, may be NULL. + * + * This function will return the requested extension OID in the + * certificate, and the critical flag for it. The extension OID will + * be stored as a string in the provided buffer. Use + * gnutls_x509_crt_get_extension_data() to extract the data. + * + * Return 0 on success. A negative value may be returned in case of + * parsing error. If you have reached the last extension available + * GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be returned. + * + **/ +int +gnutls_x509_crt_get_extension_info (gnutls_x509_crt_t cert, + int indx, + void *oid, + size_t * sizeof_oid, int *critical) +{ + int result; + char str_critical[10]; + char name[MAX_NAME_SIZE]; + int len; + + if (!cert) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + snprintf (name, sizeof (name), "tbsCertificate.extensions.?%u.extnID", + indx + 1); + + len = *sizeof_oid; + result = asn1_read_value (cert->cert, name, oid, &len); + *sizeof_oid = len; + + if (result == ASN1_ELEMENT_NOT_FOUND) + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + else if (result < 0) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + snprintf (name, sizeof (name), "tbsCertificate.extensions.?%u.critical", + indx + 1); + len = sizeof (str_critical); + result = asn1_read_value (cert->cert, name, str_critical, &len); + if (result < 0) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + if (critical) + { + if (str_critical[0] == 'T') + *critical = 1; + else + *critical = 0; + } + + return 0; + +} + +/** + * gnutls_x509_crt_get_extension_data - Get the specified extension data + * @cert: should contain a gnutls_x509_crt_t structure + * @indx: Specifies which extension OID to send. Use zero to get the first one. + * @data: a pointer to a structure to hold the data (may be null) + * @sizeof_data: initially holds the size of @oid + * + * This function will return the requested extension data in the + * certificate. The extension data will be stored as a string in the + * provided buffer. + * + * Use gnutls_x509_crt_get_extension_info() to extract the OID and + * critical flag. Use gnutls_x509_crt_get_extension_by_oid() instead, + * if you want to get data indexed by the extension OID rather than + * sequence. + * + * Return 0 on success. A negative value may be returned in case of + * parsing error. If you have reached the last extension available + * GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be returned. + **/ +int +gnutls_x509_crt_get_extension_data (gnutls_x509_crt_t cert, + int indx, + void *data, size_t * sizeof_data) +{ + int result, len; + char name[MAX_NAME_SIZE]; + + if (!cert) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + snprintf (name, sizeof (name), "tbsCertificate.extensions.?%u.extnValue", + indx + 1); + + len = *sizeof_data; + result = asn1_read_value (cert->cert, name, data, &len); + *sizeof_data = len; + + if (result == ASN1_ELEMENT_NOT_FOUND) + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + else if (result < 0) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + return 0; +} + +static int +_gnutls_x509_crt_get_raw_dn2 (gnutls_x509_crt_t cert, + const char *whom, gnutls_datum_t * start) +{ + ASN1_TYPE c2 = ASN1_TYPE_EMPTY; + int result, len1; + int start1, end1; + gnutls_datum_t signed_data = { NULL, + 0 + }; + + /* get the issuer of 'cert' + */ + if ((result = + asn1_create_element (_gnutls_get_pkix (), "PKIX1.TBSCertificate", + &c2)) != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + result = _gnutls_x509_get_signed_data (cert->cert, "tbsCertificate", + &signed_data); + if (result < 0) + { + gnutls_assert (); + goto cleanup; + } + + result = asn1_der_decoding (&c2, signed_data.data, signed_data.size, NULL); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + asn1_delete_structure (&c2); + result = _gnutls_asn2err (result); + goto cleanup; + } + + result = asn1_der_decoding_startEnd (c2, signed_data.data, signed_data.size, + whom, &start1, &end1); + + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + len1 = end1 - start1 + 1; + + _gnutls_set_datum (start, &signed_data.data[start1], len1); + + result = 0; + +cleanup:asn1_delete_structure (&c2); + _gnutls_free_datum (&signed_data); + return result; +} + +/** + * gnutls_x509_crt_get_raw_issuer_dn - This function returns the issuer's DN DER encoded + * @cert: should contain a gnutls_x509_crt_t structure + * @start: will hold the starting point of the DN + * + * This function will return a pointer to the DER encoded DN structure + * and the length. + * + * Returns 0 on success or a negative value on error. + * + **/ +int +gnutls_x509_crt_get_raw_issuer_dn (gnutls_x509_crt_t cert, + gnutls_datum_t * start) +{ + return _gnutls_x509_crt_get_raw_dn2 (cert, "issuer", start); +} + +/** + * gnutls_x509_crt_get_raw_dn - This function returns the subject's DN DER encoded + * @cert: should contain a gnutls_x509_crt_t structure + * @start: will hold the starting point of the DN + * + * This function will return a pointer to the DER encoded DN structure and + * the length. + * + * Returns 0 on success, or a negative value on error. + * + **/ +int +gnutls_x509_crt_get_raw_dn (gnutls_x509_crt_t cert, gnutls_datum_t * start) +{ + return _gnutls_x509_crt_get_raw_dn2 (cert, "subject", start); +} + +static int +get_dn (gnutls_x509_crt_t cert, const char *whom, gnutls_x509_dn_t * dn) +{ + *dn = asn1_find_node (cert->cert, whom); + if (!*dn) + return GNUTLS_E_ASN1_ELEMENT_NOT_FOUND; + return 0; +} + +/** + * gnutls_x509_crt_get_subject: get opaque subject DN pointer + * @cert: should contain a gnutls_x509_crt_t structure + * @dn: output variable with pointer to opaque DN. + * + * Return the Certificate's Subject DN as an opaque data type. You + * may use gnutls_x509_dn_get_rdn_ava() to decode the DN. + * + * Returns: Returns 0 on success, or an error code. + **/ +int +gnutls_x509_crt_get_subject (gnutls_x509_crt_t cert, gnutls_x509_dn_t * dn) +{ + return get_dn (cert, "tbsCertificate.subject.rdnSequence", dn); +} + +/** + * gnutls_x509_crt_get_issuer: get opaque issuer DN pointer + * @cert: should contain a gnutls_x509_crt_t structure + * @dn: output variable with pointer to opaque DN + * + * Return the Certificate's Issuer DN as an opaque data type. You may + * use gnutls_x509_dn_get_rdn_ava() to decode the DN. + * + * Note that @dn points into the @cert object, and thus you may not + * deallocate @cert and continue to access @dn. + * + * Returns: Returns 0 on success, or an error code. + **/ +int +gnutls_x509_crt_get_issuer (gnutls_x509_crt_t cert, gnutls_x509_dn_t * dn) +{ + return get_dn (cert, "tbsCertificate.issuer.rdnSequence", dn); +} + +/** + * gnutls_x509_dn_get_rdn_ava: + * @dn: input variable with opaque DN pointer + * @irdn: index of RDN + * @iava: index of AVA. + * @ava: Pointer to structure which will hold output information. + * + * Get pointers to data within the DN. + * + * Note that @ava will contain pointers into the @dn structure, so you + * should not modify any data or deallocate it. Note also that the DN + * in turn points into the original certificate structure, and thus + * you may not deallocate the certificate and continue to access @dn. + * + * Returns: Returns 0 on success, or an error code. + **/ +int +gnutls_x509_dn_get_rdn_ava (gnutls_x509_dn_t dn, + int irdn, int iava, gnutls_x509_ava_st * ava) +{ + ASN1_TYPE rdn, elem; + long len; + int lenlen, remlen, ret; + char rbuf[MAX_NAME_SIZE]; + unsigned char cls, *ptr; + + iava++; + irdn++; /* 0->1, 1->2 etc */ + + snprintf (rbuf, sizeof (rbuf), "rdnSequence.?%d.?%d", irdn, iava); + rdn = asn1_find_node (dn, rbuf); + if (!rdn) + { + gnutls_assert (); + return GNUTLS_E_ASN1_ELEMENT_NOT_FOUND; + } + + snprintf (rbuf, sizeof (rbuf), "?%d.type", iava); + elem = asn1_find_node (rdn, rbuf); + if (!elem) + { + gnutls_assert (); + return GNUTLS_E_ASN1_ELEMENT_NOT_FOUND; + } + + ava->oid.data = elem->value; + ava->oid.size = elem->value_len; + + snprintf (rbuf, sizeof (rbuf), "?%d.value", iava); + elem = asn1_find_node (rdn, rbuf); + if (!elem) + { + gnutls_assert (); + return GNUTLS_E_ASN1_ELEMENT_NOT_FOUND; + } + + /* The value still has the previous tag's length bytes, plus the + * current value's tag and length bytes. Decode them. + */ + + ptr = elem->value; + remlen = elem->value_len; + len = asn1_get_length_der (ptr, remlen, &lenlen); + if (len < 0) + { + gnutls_assert (); + return GNUTLS_E_ASN1_DER_ERROR; + } + + ptr += lenlen; + remlen -= lenlen; + ret = asn1_get_tag_der (ptr, remlen, &cls, &lenlen, &ava->value_tag); + if (ret) + { + gnutls_assert (); + return _gnutls_asn2err (ret); + } + + ptr += lenlen; + remlen -= lenlen; + + ava->value.size = asn1_get_length_der (ptr, remlen, &lenlen); + if (ava->value.size < 0) + { + gnutls_assert (); + return GNUTLS_E_ASN1_DER_ERROR; + } + ava->value.data = ptr + lenlen; + + return 0; +} + +/** + * gnutls_x509_crt_get_fingerprint - This function returns the Certificate's fingerprint + * @cert: should contain a gnutls_x509_crt_t structure + * @algo: is a digest algorithm + * @buf: a pointer to a structure to hold the fingerprint (may be null) + * @sizeof_buf: initially holds the size of @buf + * + * This function will calculate and copy the certificate's fingerprint + * in the provided buffer. + * + * If the buffer is null then only the size will be filled. + * + * Returns: %GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is + * not long enough, and in that case the *sizeof_buf will be updated + * with the required size. On success 0 is returned. + **/ +int +gnutls_x509_crt_get_fingerprint (gnutls_x509_crt_t cert, + gnutls_digest_algorithm_t algo, + void *buf, size_t * sizeof_buf) +{ + opaque *cert_buf; + int cert_buf_size; + int result; + gnutls_datum_t tmp; + + if (sizeof_buf == 0 || cert == NULL) + { + return GNUTLS_E_INVALID_REQUEST; + } + + cert_buf_size = 0; + asn1_der_coding (cert->cert, "", NULL, &cert_buf_size, NULL); + + cert_buf = gnutls_alloca (cert_buf_size); + if (cert_buf == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + result = asn1_der_coding (cert->cert, "", cert_buf, &cert_buf_size, NULL); + + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + gnutls_afree (cert_buf); + return _gnutls_asn2err (result); + } + + tmp.data = cert_buf; + tmp.size = cert_buf_size; + + result = gnutls_fingerprint (algo, &tmp, buf, sizeof_buf); + gnutls_afree (cert_buf); + + return result; +} + +/** + * gnutls_x509_crt_export - This function will export the certificate + * @cert: Holds the certificate + * @format: the format of output params. One of PEM or DER. + * @output_data: will contain a certificate PEM or DER encoded + * @output_data_size: holds the size of output_data (and will be + * replaced by the actual size of parameters) + * + * This function will export the certificate to DER or PEM format. + * + * If the buffer provided is not long enough to hold the output, then + * *output_data_size is updated and GNUTLS_E_SHORT_MEMORY_BUFFER will + * be returned. + * + * If the structure is PEM encoded, it will have a header + * of "BEGIN CERTIFICATE". + * + * Return value: In case of failure a negative value will be + * returned, and 0 on success. + **/ +int +gnutls_x509_crt_export (gnutls_x509_crt_t cert, + gnutls_x509_crt_fmt_t format, + void *output_data, size_t * output_data_size) +{ + if (cert == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_export_int (cert->cert, format, "CERTIFICATE", + output_data, output_data_size); +} + +static int +rsadsa_get_key_id (gnutls_x509_crt_t crt, + int pk, + unsigned char *output_data, size_t * output_data_size) +{ + mpi_t params[MAX_PUBLIC_PARAMS_SIZE]; + int params_size = MAX_PUBLIC_PARAMS_SIZE; + int i, result = 0; + gnutls_datum_t der = { NULL, + 0 + }; + GNUTLS_HASH_HANDLE hd; + + result = _gnutls_x509_crt_get_mpis (crt, params, ¶ms_size); + if (result < 0) + { + gnutls_assert (); + return result; + } + + if (pk == GNUTLS_PK_RSA) + { + result = _gnutls_x509_write_rsa_params (params, params_size, &der); + if (result < 0) + { + gnutls_assert (); + goto cleanup; + } + } + else + return GNUTLS_E_INTERNAL_ERROR; + + hd = _gnutls_hash_init (GNUTLS_MAC_SHA1); + if (hd == GNUTLS_HASH_FAILED) + { + gnutls_assert (); + result = GNUTLS_E_INTERNAL_ERROR; + goto cleanup; + } + + _gnutls_hash (hd, der.data, der.size); + + _gnutls_hash_deinit (hd, output_data); + *output_data_size = 20; + + result = 0; + +cleanup: + + _gnutls_free_datum (&der); + + /* release all allocated MPIs + */ + for (i = 0; i < params_size; i++) + { + _gnutls_mpi_release (¶ms[i]); + } + return result; +} + +/** + * gnutls_x509_crt_get_key_id - Return unique ID of public key's parameters + * @crt: Holds the certificate + * @flags: should be 0 for now + * @output_data: will contain the key ID + * @output_data_size: holds the size of output_data (and will be + * replaced by the actual size of parameters) + * + * This function will return a unique ID the depends on the public + * key parameters. This ID can be used in checking whether a + * certificate corresponds to the given private key. + * + * If the buffer provided is not long enough to hold the output, then + * *output_data_size is updated and GNUTLS_E_SHORT_MEMORY_BUFFER will + * be returned. The output will normally be a SHA-1 hash output, + * which is 20 bytes. + * + * Return value: In case of failure a negative value will be + * returned, and 0 on success. + **/ +int +gnutls_x509_crt_get_key_id (gnutls_x509_crt_t crt, + unsigned int flags, + unsigned char *output_data, + size_t * output_data_size) +{ + int pk, result = 0; + gnutls_datum_t pubkey; + + if (crt == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + if (*output_data_size < 20) + { + gnutls_assert (); + *output_data_size = 20; + return GNUTLS_E_SHORT_MEMORY_BUFFER; + } + + pk = gnutls_x509_crt_get_pk_algorithm (crt, NULL); + if (pk < 0) + { + gnutls_assert (); + return pk; + } + + if (pk == GNUTLS_PK_RSA) + { + /* This is for compatibility with what GnuTLS has printed for + RSA/DSA before the code below was added. The code below is + applicable to all types, and it would probably be a better + idea to use it for RSA/DSA too, but doing so would break + backwards compatibility. */ + return rsadsa_get_key_id (crt, pk, output_data, output_data_size); + } + + pubkey.size = 0; + result = asn1_der_coding (crt->cert, "tbsCertificate.subjectPublicKeyInfo", + NULL, &pubkey.size, NULL); + if (result != ASN1_MEM_ERROR) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + pubkey.data = gnutls_alloca (pubkey.size); + if (pubkey.data == NULL) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + result = asn1_der_coding (crt->cert, "tbsCertificate.subjectPublicKeyInfo", + pubkey.data, &pubkey.size, NULL); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + gnutls_afree (pubkey.data); + return _gnutls_asn2err (result); + } + + result = gnutls_fingerprint (GNUTLS_DIG_SHA1, &pubkey, output_data, + output_data_size); + + gnutls_afree (pubkey.data); + + return result; +} + +#ifdef ENABLE_PKI + +/** + * gnutls_x509_crt_check_revocation - This function checks if the given certificate is revoked + * @cert: should contain a gnutls_x509_crt_t structure + * @crl_list: should contain a list of gnutls_x509_crl_t structures + * @crl_list_length: the length of the crl_list + * + * This function will return check if the given certificate is + * revoked. It is assumed that the CRLs have been verified before. + * + * Returns: 0 if the certificate is NOT revoked, and 1 if it is. A + * negative value is returned on error. + **/ +int +gnutls_x509_crt_check_revocation (gnutls_x509_crt_t cert, + const gnutls_x509_crl_t * crl_list, + int crl_list_length) +{ + opaque serial[64]; + opaque cert_serial[64]; + size_t serial_size, cert_serial_size; + int ncerts, ret, i, j; + gnutls_datum_t dn1, dn2; + + if (cert == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + for (j = 0; j < crl_list_length; j++) + { /* do for all the crls */ + + /* Step 1. check if issuer's DN match + */ + ret = _gnutls_x509_crl_get_raw_issuer_dn (crl_list[j], &dn1); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + ret = gnutls_x509_crt_get_raw_issuer_dn (cert, &dn2); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + ret = _gnutls_x509_compare_raw_dn (&dn1, &dn2); + _gnutls_free_datum (&dn1); + _gnutls_free_datum (&dn2); + if (ret == 0) + { + /* issuers do not match so don't even + * bother checking. + */ + continue; + } + + /* Step 2. Read the certificate's serial number + */ + cert_serial_size = sizeof (cert_serial); + ret = gnutls_x509_crt_get_serial (cert, cert_serial, &cert_serial_size); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + /* Step 3. cycle through the CRL serials and compare with + * certificate serial we have. + */ + + ncerts = gnutls_x509_crl_get_crt_count (crl_list[j]); + if (ncerts < 0) + { + gnutls_assert (); + return ncerts; + } + + for (i = 0; i < ncerts; i++) + { + serial_size = sizeof (serial); + ret = gnutls_x509_crl_get_crt_serial (crl_list[j], i, serial, + &serial_size, NULL); + + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + if (serial_size == cert_serial_size) + { + if (memcmp (serial, cert_serial, serial_size) == 0) + { + /* serials match */ + return 1; /* revoked! */ + } + } + } + + } + return 0; /* not revoked. */ +} + +/** + * gnutls_x509_crt_verify_data - This function will verify the given signed data. + * @crt: Holds the certificate + * @flags: should be 0 for now + * @data: holds the data to be signed + * @signature: contains the signature + * + * This function will verify the given signed data, using the + * parameters from the certificate. + * + * Returns: In case of a verification failure 0 is returned, and 1 on + * success. + **/ +int +gnutls_x509_crt_verify_data (gnutls_x509_crt_t crt, + unsigned int flags, + const gnutls_datum_t * data, + const gnutls_datum_t * signature) +{ + int result; + + if (crt == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + result = _gnutls_x509_verify_signature (data, signature, crt); + if (result < 0) + { + gnutls_assert (); + return 0; + } + + return result; +} + +/** + * gnutls_x509_crt_get_crl_dist_points - This function returns the CRL distribution points + * @cert: should contain a gnutls_x509_crt_t structure + * @seq: specifies the sequence number of the distribution point (0 for the first one, 1 for the second etc.) + * @ret: is the place where the distribution point will be copied to + * @ret_size: holds the size of ret. + * @reason_flags: Revocation reasons flags. + * @critical: will be non zero if the extension is marked as critical (may be null) + * + * This function will return the CRL distribution points (2.5.29.31), + * contained in the given certificate. + * + * @reason_flags should be an ORed sequence of + * GNUTLS_CRL_REASON_UNUSED, GNUTLS_CRL_REASON_KEY_COMPROMISE, + * GNUTLS_CRL_REASON_CA_COMPROMISE, + * GNUTLS_CRL_REASON_AFFILIATION_CHANGED, + * GNUTLS_CRL_REASON_SUPERSEEDED, + * GNUTLS_CRL_REASON_CESSATION_OF_OPERATION, + * GNUTLS_CRL_REASON_CERTIFICATE_HOLD, + * GNUTLS_CRL_REASON_PRIVILEGE_WITHDRAWN, + * GNUTLS_CRL_REASON_AA_COMPROMISE, or zero for all possible reasons. + * + * This is specified in X509v3 Certificate Extensions. GNUTLS will + * return the distribution point type, or a negative error code on + * error. + * + * Returns %GNUTLS_E_SHORT_MEMORY_BUFFER and updates &@ret_size if + * &@ret_size is not enough to hold the distribution point, or the + * type of the distribution point if everything was ok. The type is + * one of the enumerated %gnutls_x509_subject_alt_name_t. If the + * certificate does not have an Alternative name with the specified + * sequence number then %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE is + * returned. + **/ +int +gnutls_x509_crt_get_crl_dist_points (gnutls_x509_crt_t cert, + unsigned int seq, + void *ret, + size_t * ret_size, + unsigned int *reason_flags, + unsigned int *critical) +{ + int result; + gnutls_datum_t dist_points = { NULL, + 0 + }; + ASN1_TYPE c2 = ASN1_TYPE_EMPTY; + char name[MAX_NAME_SIZE]; + int len; + gnutls_x509_subject_alt_name_t type; + uint8_t reasons[2]; + + if (cert == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + if (*ret_size > 0 && ret) + memset (ret, 0, *ret_size); + else + *ret_size = 0; + + if (reason_flags) + *reason_flags = 0; + + result = _gnutls_x509_crt_get_extension (cert, "2.5.29.31", 0, &dist_points, + critical); + if (result < 0) + { + return result; + } + + if (dist_points.size == 0 || dist_points.data == NULL) + { + gnutls_assert (); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + result = + asn1_create_element (_gnutls_get_pkix (), "PKIX1.CRLDistributionPoints", + &c2); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + _gnutls_free_datum (&dist_points); + return _gnutls_asn2err (result); + } + + result = asn1_der_decoding (&c2, dist_points.data, dist_points.size, NULL); + _gnutls_free_datum (&dist_points); + + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + asn1_delete_structure (&c2); + return _gnutls_asn2err (result); + } + + /* Return the different names from the first CRLDistr. point. + * The whole thing is a mess. + */ + _gnutls_str_cpy (name, sizeof (name), "?1.distributionPoint.fullName"); + + result = parse_general_name (c2, name, seq, ret, ret_size, NULL, 0); + if (result < 0) + { + asn1_delete_structure (&c2); + return result; + } + + type = result; + + /* Read the CRL reasons. + */ + if (reason_flags) + { + _gnutls_str_cpy (name, sizeof (name), "?1.reasons"); + + reasons[0] = reasons[1] = 0; + + len = sizeof (reasons); + result = asn1_read_value (c2, name, reasons, &len); + + if (result != ASN1_VALUE_NOT_FOUND && result != ASN1_SUCCESS) + { + gnutls_assert (); + asn1_delete_structure (&c2); + return _gnutls_asn2err (result); + } + + *reason_flags = reasons[0] | (reasons[1] << 8); + } + + return type; +} + +/** + * gnutls_x509_crt_get_key_purpose_oid - This function returns the Certificate's key purpose OIDs + * @cert: should contain a gnutls_x509_crt_t structure + * @indx: This specifies which OID to return. Use zero to get the first one. + * @oid: a pointer to a buffer to hold the OID (may be null) + * @sizeof_oid: initially holds the size of @oid + * + * This function will extract the key purpose OIDs of the Certificate + * specified by the given index. These are stored in the Extended Key + * Usage extension (2.5.29.37) See the GNUTLS_KP_* definitions for + * human readable names. + * + * If @oid is null then only the size will be filled. + * + * Returns: %GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is + * not long enough, and in that case the *sizeof_oid will be updated + * with the required size. On success 0 is returned. + **/ +int +gnutls_x509_crt_get_key_purpose_oid (gnutls_x509_crt_t cert, + int indx, + void *oid, + size_t * sizeof_oid, + unsigned int *critical) +{ + char tmpstr[MAX_NAME_SIZE]; + int result, len; + gnutls_datum_t id; + ASN1_TYPE c2 = ASN1_TYPE_EMPTY; + + if (cert == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + if (oid) + memset (oid, 0, *sizeof_oid); + else + *sizeof_oid = 0; + + if ((result = _gnutls_x509_crt_get_extension (cert, "2.5.29.37", 0, &id, + critical)) < 0) + { + return result; + } + + if (id.size == 0 || id.data == NULL) + { + gnutls_assert (); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + result = + asn1_create_element (_gnutls_get_pkix (), "PKIX1.ExtKeyUsageSyntax", &c2); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + _gnutls_free_datum (&id); + return _gnutls_asn2err (result); + } + + result = asn1_der_decoding (&c2, id.data, id.size, NULL); + _gnutls_free_datum (&id); + + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + asn1_delete_structure (&c2); + return _gnutls_asn2err (result); + } + + indx++; + /* create a string like "?1" + */ + snprintf (tmpstr, sizeof (tmpstr), "?%u", indx); + + len = *sizeof_oid; + result = asn1_read_value (c2, tmpstr, oid, &len); + + *sizeof_oid = len; + asn1_delete_structure (&c2); + + if (result == ASN1_VALUE_NOT_FOUND || result == ASN1_ELEMENT_NOT_FOUND) + { + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + return 0; + +} + +/** + * gnutls_x509_crt_get_pk_rsa_raw - This function will export the RSA public key + * @crt: Holds the certificate + * @m: will hold the modulus + * @e: will hold the public exponent + * + * This function will export the RSA public key's parameters found in + * the given structure. The new parameters will be allocated using + * gnutls_malloc() and will be stored in the appropriate datum. + * + * Returns: %GNUTLS_E_SUCCESS on success, otherwise an error. + **/ +int +gnutls_x509_crt_get_pk_rsa_raw (gnutls_x509_crt_t crt, + gnutls_datum_t * m, gnutls_datum_t * e) +{ + int ret; + mpi_t params[MAX_PUBLIC_PARAMS_SIZE]; + int params_size = MAX_PUBLIC_PARAMS_SIZE; + int i; + + if (crt == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = gnutls_x509_crt_get_pk_algorithm (crt, NULL); + if (ret != GNUTLS_PK_RSA) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = _gnutls_x509_crt_get_mpis (crt, params, ¶ms_size); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + ret = _gnutls_mpi_dprint (m, params[0]); + if (ret < 0) + { + gnutls_assert (); + goto cleanup; + } + + ret = _gnutls_mpi_dprint (e, params[1]); + if (ret < 0) + { + gnutls_assert (); + _gnutls_free_datum (m); + goto cleanup; + } + + ret = 0; + +cleanup:for (i = 0; i < params_size; i++) + { + _gnutls_mpi_release (¶ms[i]); + } + return ret; +} + +/** + * gnutls_x509_crt_get_pk_dsa_raw - This function will export the DSA public key + * @crt: Holds the certificate + * @p: will hold the p + * @q: will hold the q + * @g: will hold the g + * @y: will hold the y + * + * This function will export the DSA public key's parameters found in + * the given certificate. The new parameters will be allocated using + * gnutls_malloc() and will be stored in the appropriate datum. + * + * Returns: %GNUTLS_E_SUCCESS on success, otherwise an error. + **/ +int +gnutls_x509_crt_get_pk_dsa_raw (gnutls_x509_crt_t crt, + gnutls_datum_t * p, + gnutls_datum_t * q, + gnutls_datum_t * g, gnutls_datum_t * y) +{ + int ret; + mpi_t params[MAX_PUBLIC_PARAMS_SIZE]; + int params_size = MAX_PUBLIC_PARAMS_SIZE; + int i; + + if (crt == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = gnutls_x509_crt_get_pk_algorithm (crt, NULL); + + ret = _gnutls_x509_crt_get_mpis (crt, params, ¶ms_size); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + /* P */ + ret = _gnutls_mpi_dprint (p, params[0]); + if (ret < 0) + { + gnutls_assert (); + goto cleanup; + } + + /* Q */ + ret = _gnutls_mpi_dprint (q, params[1]); + if (ret < 0) + { + gnutls_assert (); + _gnutls_free_datum (p); + goto cleanup; + } + + /* G */ + ret = _gnutls_mpi_dprint (g, params[2]); + if (ret < 0) + { + gnutls_assert (); + _gnutls_free_datum (p); + _gnutls_free_datum (q); + goto cleanup; + } + + /* Y */ + ret = _gnutls_mpi_dprint (y, params[3]); + if (ret < 0) + { + gnutls_assert (); + _gnutls_free_datum (p); + _gnutls_free_datum (g); + _gnutls_free_datum (q); + goto cleanup; + } + + ret = 0; + +cleanup:for (i = 0; i < params_size; i++) + { + _gnutls_mpi_release (¶ms[i]); + } + return ret; + +} + +#endif + +/** + * gnutls_x509_crt_list_import - This function will import a PEM encoded certificate list + * @certs: The structures to store the parsed certificate. Must not be initialized. + * @cert_max: Initially must hold the maximum number of certs. It will be updated with the number of certs available. + * @data: The PEM encoded certificate. + * @format: One of DER or PEM. + * @flags: must be zero or an OR'd sequence of gnutls_certificate_import_flags. + * + * This function will convert the given PEM encoded certificate list + * to the native gnutls_x509_crt_t format. The output will be stored + * in @certs. They will be automatically initialized. + * + * If the Certificate is PEM encoded it should have a header of "X509 + * CERTIFICATE", or "CERTIFICATE". + * + * Returns: the number of certificates read or a negative error value. + **/ +int +gnutls_x509_crt_list_import (gnutls_x509_crt_t * certs, + unsigned int *cert_max, + const gnutls_datum_t * data, + gnutls_x509_crt_fmt_t format, unsigned int flags) +{ + int size; + const char *ptr; + gnutls_datum_t tmp; + int ret, nocopy = 0; + unsigned int count = 0, j; + + if (format == GNUTLS_X509_FMT_DER) + { + if (*cert_max < 1) + { + *cert_max = 1; + return GNUTLS_E_SHORT_MEMORY_BUFFER; + } + + count = 1; /* import only the first one */ + + ret = gnutls_x509_crt_init (&certs[0]); + if (ret < 0) + { + gnutls_assert (); + goto error; + } + + ret = gnutls_x509_crt_import (certs[0], data, format); + if (ret < 0) + { + gnutls_assert (); + goto error; + } + + *cert_max = 1; + return 1; + } + + /* move to the certificate + */ + ptr = memmem (data->data, data->size, + PEM_CERT_SEP, sizeof (PEM_CERT_SEP) - 1); + if (ptr == NULL) + ptr = memmem (data->data, data->size, + PEM_CERT_SEP2, sizeof (PEM_CERT_SEP2) - 1); + + if (ptr == NULL) + { + gnutls_assert (); + return GNUTLS_E_BASE64_DECODING_ERROR; + } + size = data->size - (ptr - (char *) data->data); + + count = 0; + + do + { + if (count >= *cert_max) + { + if (!(flags & GNUTLS_X509_CRT_LIST_IMPORT_FAIL_IF_EXCEED)) + break; + else + nocopy = 1; + } + + if (!nocopy) + { + ret = gnutls_x509_crt_init (&certs[count]); + if (ret < 0) + { + gnutls_assert (); + goto error; + } + + tmp.data = (void *) ptr; + tmp.size = size; + + ret = + gnutls_x509_crt_import (certs[count], &tmp, GNUTLS_X509_FMT_PEM); + if (ret < 0) + { + gnutls_assert (); + goto error; + } + } + + /* now we move ptr after the pem header + */ + ptr++; + /* find the next certificate (if any) + */ + size = data->size - (ptr - (char *) data->data); + + if (size > 0) + { + char *ptr2; + + ptr2 = memmem (ptr, size, PEM_CERT_SEP, sizeof (PEM_CERT_SEP) - 1); + if (ptr2 == NULL) + ptr2 = + memmem (ptr, size, PEM_CERT_SEP2, sizeof (PEM_CERT_SEP2) - 1); + + ptr = ptr2; + } + else + ptr = NULL; + + count++; + } + while (ptr != NULL); + + *cert_max = count; + + if (nocopy == 0) + return count; + else + return GNUTLS_E_SHORT_MEMORY_BUFFER; + +error:for (j = 0; j < count; j++) + gnutls_x509_crt_deinit (certs[j]); + return ret; +} diff --git a/src/daemon/https/x509/x509.h b/src/daemon/https/x509/x509.h new file mode 100644 index 00000000..c9bb22ef --- /dev/null +++ b/src/daemon/https/x509/x509.h @@ -0,0 +1,928 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2007 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +#ifndef X509_H +# define X509_H + +#define MIN(X,Y) ((X) > (Y) ? (Y) : (X)); + +#ifdef __cplusplus +extern "C" + { +#endif + +#include +// TODO #include "libtasn1.h" +#include "gnutls_mpi.h" + +/* Some OIDs usually found in Distinguished names, or + * in Subject Directory Attribute extensions. + */ +#define GNUTLS_OID_X520_COUNTRY_NAME "2.5.4.6" +#define GNUTLS_OID_X520_ORGANIZATION_NAME "2.5.4.10" +#define GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME "2.5.4.11" +#define GNUTLS_OID_X520_COMMON_NAME "2.5.4.3" +#define GNUTLS_OID_X520_LOCALITY_NAME "2.5.4.7" +#define GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME "2.5.4.8" + +#define GNUTLS_OID_X520_INITIALS "2.5.4.43" +#define GNUTLS_OID_X520_GENERATION_QUALIFIER "2.5.4.44" +#define GNUTLS_OID_X520_SURNAME "2.5.4.4" +#define GNUTLS_OID_X520_GIVEN_NAME "2.5.4.42" +#define GNUTLS_OID_X520_TITLE "2.5.4.12" +#define GNUTLS_OID_X520_DN_QUALIFIER "2.5.4.46" +#define GNUTLS_OID_X520_PSEUDONYM "2.5.4.65" + +#define GNUTLS_OID_LDAP_DC "0.9.2342.19200300.100.1.25" +#define GNUTLS_OID_LDAP_UID "0.9.2342.19200300.100.1.1" + +/* The following should not be included in DN. + */ +#define GNUTLS_OID_PKCS9_EMAIL "1.2.840.113549.1.9.1" + +#define GNUTLS_OID_PKIX_DATE_OF_BIRTH "1.3.6.1.5.5.7.9.1" +#define GNUTLS_OID_PKIX_PLACE_OF_BIRTH "1.3.6.1.5.5.7.9.2" +#define GNUTLS_OID_PKIX_GENDER "1.3.6.1.5.5.7.9.3" +#define GNUTLS_OID_PKIX_COUNTRY_OF_CITIZENSHIP "1.3.6.1.5.5.7.9.4" +#define GNUTLS_OID_PKIX_COUNTRY_OF_RESIDENCE "1.3.6.1.5.5.7.9.5" + +/* Key purpose Object Identifiers. + */ +#define GNUTLS_KP_TLS_WWW_SERVER "1.3.6.1.5.5.7.3.1" +#define GNUTLS_KP_TLS_WWW_CLIENT "1.3.6.1.5.5.7.3.2" +#define GNUTLS_KP_CODE_SIGNING "1.3.6.1.5.5.7.3.3" +#define GNUTLS_KP_EMAIL_PROTECTION "1.3.6.1.5.5.7.3.4" +#define GNUTLS_KP_TIME_STAMPING "1.3.6.1.5.5.7.3.8" +#define GNUTLS_KP_OCSP_SIGNING "1.3.6.1.5.5.7.3.9" +#define GNUTLS_KP_ANY "2.5.29.37.0" + +/* Certificate handling functions. + */ +typedef enum gnutls_certificate_import_flags + { + /* Fail if the certificates in the buffer are more than the space + * allocated for certificates. The error code will be + * GNUTLS_E_SHORT_MEMORY_BUFFER. + */ + GNUTLS_X509_CRT_LIST_IMPORT_FAIL_IF_EXCEED = 1 + } gnutls_certificate_import_flags; + +int gnutls_x509_crt_init(gnutls_x509_crt_t * cert); +void gnutls_x509_crt_deinit(gnutls_x509_crt_t cert); +int gnutls_x509_crt_import(gnutls_x509_crt_t cert, + const gnutls_datum_t * data, + gnutls_x509_crt_fmt_t format); +int gnutls_x509_crt_list_import(gnutls_x509_crt_t * certs, + unsigned int *cert_max, + const gnutls_datum_t * data, + gnutls_x509_crt_fmt_t format, + unsigned int flags); +int gnutls_x509_crt_export(gnutls_x509_crt_t cert, + gnutls_x509_crt_fmt_t format, + void *output_data, + size_t * output_data_size); +int gnutls_x509_crt_get_issuer_dn(gnutls_x509_crt_t cert, + char *buf, + size_t * sizeof_buf); +int gnutls_x509_crt_get_issuer_dn_oid(gnutls_x509_crt_t cert, + int indx, + void *oid, + size_t * sizeof_oid); +int gnutls_x509_crt_get_issuer_dn_by_oid(gnutls_x509_crt_t cert, + const char *oid, + int indx, + unsigned int raw_flag, + void *buf, + size_t * sizeof_buf); +int gnutls_x509_crt_get_dn(gnutls_x509_crt_t cert, + char *buf, + size_t * sizeof_buf); +int gnutls_x509_crt_get_dn_oid(gnutls_x509_crt_t cert, + int indx, + void *oid, + size_t * sizeof_oid); +int gnutls_x509_crt_get_dn_by_oid(gnutls_x509_crt_t cert, + const char *oid, + int indx, + unsigned int raw_flag, + void *buf, + size_t * sizeof_buf); +int gnutls_x509_crt_check_hostname(gnutls_x509_crt_t cert, + const char *hostname); + +int gnutls_x509_crt_get_signature_algorithm(gnutls_x509_crt_t cert); +int gnutls_x509_crt_get_signature(gnutls_x509_crt_t cert, + char *sig, + size_t *sizeof_sig); +int gnutls_x509_crt_get_version(gnutls_x509_crt_t cert); +int gnutls_x509_crt_get_key_id(gnutls_x509_crt_t crt, + unsigned int flags, + unsigned char *output_data, + size_t * output_data_size); + +int gnutls_x509_crt_set_authority_key_id(gnutls_x509_crt_t cert, + const void *id, + size_t id_size); +int gnutls_x509_crt_get_authority_key_id(gnutls_x509_crt_t cert, + void *ret, + size_t * ret_size, + unsigned int *critical); + +int gnutls_x509_crt_get_subject_key_id(gnutls_x509_crt_t cert, + void *ret, + size_t * ret_size, + unsigned int *critical); + +#define GNUTLS_CRL_REASON_UNUSED 128 +#define GNUTLS_CRL_REASON_KEY_COMPROMISE 64 +#define GNUTLS_CRL_REASON_CA_COMPROMISE 32 +#define GNUTLS_CRL_REASON_AFFILIATION_CHANGED 16 +#define GNUTLS_CRL_REASON_SUPERSEEDED 8 +#define GNUTLS_CRL_REASON_CESSATION_OF_OPERATION 4 +#define GNUTLS_CRL_REASON_CERTIFICATE_HOLD 2 +#define GNUTLS_CRL_REASON_PRIVILEGE_WITHDRAWN 1 +#define GNUTLS_CRL_REASON_AA_COMPROMISE 32768 + +int gnutls_x509_crt_get_crl_dist_points(gnutls_x509_crt_t cert, + unsigned int seq, + void *ret, + size_t * ret_size, + unsigned int *reason_flags, + unsigned int *critical); +int gnutls_x509_crt_set_crl_dist_points(gnutls_x509_crt_t crt, + gnutls_x509_subject_alt_name_t + type, + const void *data_string, + unsigned int reason_flags); +int gnutls_x509_crt_cpy_crl_dist_points(gnutls_x509_crt_t dst, + gnutls_x509_crt_t src); + +time_t gnutls_x509_crt_get_activation_time(gnutls_x509_crt_t cert); +time_t gnutls_x509_crt_get_expiration_time(gnutls_x509_crt_t cert); +int gnutls_x509_crt_get_serial(gnutls_x509_crt_t cert, + void *result, + size_t * result_size); + +int gnutls_x509_crt_get_pk_algorithm(gnutls_x509_crt_t cert, + unsigned int *bits); +int gnutls_x509_crt_get_pk_rsa_raw(gnutls_x509_crt_t crt, + gnutls_datum_t * m, + gnutls_datum_t * e); +int gnutls_x509_crt_get_pk_dsa_raw(gnutls_x509_crt_t crt, + gnutls_datum_t * p, + gnutls_datum_t * q, + gnutls_datum_t * g, + gnutls_datum_t * y); + +int gnutls_x509_crt_get_subject_alt_name(gnutls_x509_crt_t cert, + unsigned int seq, + void *ret, + size_t * ret_size, + unsigned int *critical); +int gnutls_x509_crt_get_subject_alt_name2(gnutls_x509_crt_t cert, + unsigned int seq, + void *ret, + size_t * ret_size, + unsigned int* ret_type, + unsigned int *critical); + +int gnutls_x509_crt_get_subject_alt_othername_oid(gnutls_x509_crt_t cert, + unsigned int seq, + void *ret, + size_t * ret_size); + +int gnutls_x509_crt_get_ca_status(gnutls_x509_crt_t cert, + unsigned int *critical); +int gnutls_x509_crt_get_basic_constraints(gnutls_x509_crt_t cert, + unsigned int *critical, + int *ca, + int *pathlen); + +/* The key_usage flags are defined in gnutls.h. They are the + * GNUTLS_KEY_* definitions. + */ +int gnutls_x509_crt_get_key_usage(gnutls_x509_crt_t cert, + unsigned int *key_usage, + unsigned int *critical); +int gnutls_x509_crt_set_key_usage(gnutls_x509_crt_t crt, + unsigned int usage); + +int gnutls_x509_crt_get_proxy(gnutls_x509_crt_t cert, + unsigned int *critical, + int *pathlen, + char **policyLanguage, + char **policy, + size_t *sizeof_policy); + +int gnutls_x509_dn_oid_known(const char *oid); + +/* Read extensions by OID. */ +int gnutls_x509_crt_get_extension_oid(gnutls_x509_crt_t cert, + int indx, + void *oid, + size_t * sizeof_oid); +int gnutls_x509_crt_get_extension_by_oid(gnutls_x509_crt_t cert, + const char *oid, + int indx, + void *buf, + size_t * sizeof_buf, + unsigned int *critical); + +/* Read extensions by sequence number. */ +int gnutls_x509_crt_get_extension_info(gnutls_x509_crt_t cert, + int indx, + void *oid, + size_t * sizeof_oid, + int *critical); +int gnutls_x509_crt_get_extension_data(gnutls_x509_crt_t cert, + int indx, + void *data, + size_t * sizeof_data); + +int gnutls_x509_crt_set_extension_by_oid(gnutls_x509_crt_t crt, + const char *oid, + const void *buf, + size_t sizeof_buf, + unsigned int critical); + +/* X.509 Certificate writing. + */ +int gnutls_x509_crt_set_dn_by_oid(gnutls_x509_crt_t crt, + const char *oid, + unsigned int raw_flag, + const void *name, + unsigned int sizeof_name); +int gnutls_x509_crt_set_issuer_dn_by_oid(gnutls_x509_crt_t crt, + const char *oid, + unsigned int raw_flag, + const void *name, + unsigned int sizeof_name); +int gnutls_x509_crt_set_version(gnutls_x509_crt_t crt, + unsigned int version); +int gnutls_x509_crt_set_key(gnutls_x509_crt_t crt, + gnutls_x509_privkey_t key); +int gnutls_x509_crt_set_ca_status(gnutls_x509_crt_t crt, + unsigned int ca); +int gnutls_x509_crt_set_basic_constraints(gnutls_x509_crt_t crt, + unsigned int ca, + int pathLenConstraint); +int gnutls_x509_crt_set_subject_alternative_name(gnutls_x509_crt_t crt, + gnutls_x509_subject_alt_name_t + type, + const char *data_string); +int gnutls_x509_crt_sign(gnutls_x509_crt_t crt, + gnutls_x509_crt_t issuer, + gnutls_x509_privkey_t issuer_key); +int gnutls_x509_crt_sign2(gnutls_x509_crt_t crt, + gnutls_x509_crt_t issuer, + gnutls_x509_privkey_t issuer_key, + gnutls_digest_algorithm_t, + unsigned int flags); +int gnutls_x509_crt_set_activation_time(gnutls_x509_crt_t cert, + time_t act_time); +int gnutls_x509_crt_set_expiration_time(gnutls_x509_crt_t cert, + time_t exp_time); +int gnutls_x509_crt_set_serial(gnutls_x509_crt_t cert, + const void *serial, + size_t serial_size); + +int gnutls_x509_crt_set_subject_key_id(gnutls_x509_crt_t cert, + const void *id, + size_t id_size); + +int gnutls_x509_crt_set_proxy_dn(gnutls_x509_crt_t crt, + gnutls_x509_crt_t eecrt, + unsigned int raw_flag, + const void *name, + unsigned int sizeof_name); +int gnutls_x509_crt_set_proxy(gnutls_x509_crt_t crt, + int pathLenConstraint, + const char *policyLanguage, + const char *policy, + size_t sizeof_policy); + +typedef enum gnutls_certificate_print_formats + { + GNUTLS_X509_CRT_FULL, + GNUTLS_X509_CRT_ONELINE, + GNUTLS_X509_CRT_UNSIGNED_FULL + } gnutls_certificate_print_formats_t; + +int gnutls_x509_crt_print(gnutls_x509_crt_t cert, + gnutls_certificate_print_formats_t format, + gnutls_datum_t *out); +int gnutls_x509_crl_print(gnutls_x509_crl_t crl, + gnutls_certificate_print_formats_t format, + gnutls_datum_t *out); + +/* Access to internal Certificate fields. + */ +int gnutls_x509_crt_get_raw_issuer_dn(gnutls_x509_crt_t cert, + gnutls_datum_t * start); +int gnutls_x509_crt_get_raw_dn(gnutls_x509_crt_t cert, + gnutls_datum_t * start); + +/* RDN handling. + */ +int gnutls_x509_rdn_get(const gnutls_datum_t * idn, + char *buf, + size_t * sizeof_buf); +int gnutls_x509_rdn_get_oid(const gnutls_datum_t * idn, + int indx, + void *buf, + size_t * sizeof_buf); + +int gnutls_x509_rdn_get_by_oid(const gnutls_datum_t * idn, + const char *oid, + int indx, + unsigned int raw_flag, + void *buf, + size_t * sizeof_buf); + +typedef void *gnutls_x509_dn_t; + +typedef struct gnutls_x509_ava_st + { + gnutls_datum_t oid; + gnutls_datum_t value; + unsigned long value_tag; + } gnutls_x509_ava_st; + +int gnutls_x509_crt_get_subject(gnutls_x509_crt_t cert, + gnutls_x509_dn_t *dn); +int gnutls_x509_crt_get_issuer(gnutls_x509_crt_t cert, + gnutls_x509_dn_t *dn); +int gnutls_x509_dn_get_rdn_ava(gnutls_x509_dn_t dn, + int irdn, + int iava, + gnutls_x509_ava_st *avast); + +/* CRL handling functions. + */ +int gnutls_x509_crl_init(gnutls_x509_crl_t * crl); +void gnutls_x509_crl_deinit(gnutls_x509_crl_t crl); + +int gnutls_x509_crl_import(gnutls_x509_crl_t crl, + const gnutls_datum_t * data, + gnutls_x509_crt_fmt_t format); +int gnutls_x509_crl_export(gnutls_x509_crl_t crl, + gnutls_x509_crt_fmt_t format, + void *output_data, + size_t * output_data_size); + +int gnutls_x509_crl_get_issuer_dn(const gnutls_x509_crl_t crl, + char *buf, + size_t * sizeof_buf); +int gnutls_x509_crl_get_issuer_dn_by_oid(gnutls_x509_crl_t crl, + const char *oid, + int indx, + unsigned int raw_flag, + void *buf, + size_t * sizeof_buf); +int gnutls_x509_crl_get_dn_oid(gnutls_x509_crl_t crl, + int indx, + void *oid, + size_t * sizeof_oid); + +int gnutls_x509_crl_get_signature_algorithm(gnutls_x509_crl_t crl); +int gnutls_x509_crl_get_signature(gnutls_x509_crl_t crl, + char *sig, + size_t *sizeof_sig); +int gnutls_x509_crl_get_version(gnutls_x509_crl_t crl); + +time_t gnutls_x509_crl_get_this_update(gnutls_x509_crl_t crl); +time_t gnutls_x509_crl_get_next_update(gnutls_x509_crl_t crl); + +int gnutls_x509_crl_get_crt_count(gnutls_x509_crl_t crl); +int gnutls_x509_crl_get_crt_serial(gnutls_x509_crl_t crl, + int indx, + unsigned char *serial, + size_t * serial_size, + time_t * t); +#define gnutls_x509_crl_get_certificate_count gnutls_x509_crl_get_crt_count +#define gnutls_x509_crl_get_certificate gnutls_x509_crl_get_crt_serial + +int gnutls_x509_crl_check_issuer(gnutls_x509_crl_t crl, + gnutls_x509_crt_t issuer); + +/* CRL writing. + */ +int gnutls_x509_crl_set_version(gnutls_x509_crl_t crl, + unsigned int version); +int gnutls_x509_crl_sign(gnutls_x509_crl_t crl, + gnutls_x509_crt_t issuer, + gnutls_x509_privkey_t issuer_key); +int gnutls_x509_crl_sign2(gnutls_x509_crl_t crl, + gnutls_x509_crt_t issuer, + gnutls_x509_privkey_t issuer_key, + gnutls_digest_algorithm_t, + unsigned int flags); +int gnutls_x509_crl_set_this_update(gnutls_x509_crl_t crl, + time_t act_time); +int gnutls_x509_crl_set_next_update(gnutls_x509_crl_t crl, + time_t exp_time); +int gnutls_x509_crl_set_crt_serial(gnutls_x509_crl_t crl, + const void *serial, + size_t serial_size, + time_t revocation_time); +int gnutls_x509_crl_set_crt(gnutls_x509_crl_t crl, + gnutls_x509_crt_t crt, + time_t revocation_time); + +/* PKCS7 structures handling + */ +struct gnutls_pkcs7_int; +typedef struct gnutls_pkcs7_int *gnutls_pkcs7_t; + +int gnutls_pkcs7_init(gnutls_pkcs7_t * pkcs7); +void gnutls_pkcs7_deinit(gnutls_pkcs7_t pkcs7); +int gnutls_pkcs7_import(gnutls_pkcs7_t pkcs7, + const gnutls_datum_t * data, + gnutls_x509_crt_fmt_t format); +int gnutls_pkcs7_export(gnutls_pkcs7_t pkcs7, + gnutls_x509_crt_fmt_t format, + void *output_data, + size_t * output_data_size); + +int gnutls_pkcs7_get_crt_count(gnutls_pkcs7_t pkcs7); +int gnutls_pkcs7_get_crt_raw(gnutls_pkcs7_t pkcs7, + int indx, + void *certificate, + size_t * certificate_size); + +int gnutls_pkcs7_set_crt_raw(gnutls_pkcs7_t pkcs7, + const gnutls_datum_t * crt); +int gnutls_pkcs7_set_crt(gnutls_pkcs7_t pkcs7, + gnutls_x509_crt_t crt); +int gnutls_pkcs7_delete_crt(gnutls_pkcs7_t pkcs7, + int indx); + +int gnutls_pkcs7_get_crl_raw(gnutls_pkcs7_t pkcs7, + int indx, + void *crl, + size_t * crl_size); +int gnutls_pkcs7_get_crl_count(gnutls_pkcs7_t pkcs7); + +int gnutls_pkcs7_set_crl_raw(gnutls_pkcs7_t pkcs7, + const gnutls_datum_t * crt); +int gnutls_pkcs7_set_crl(gnutls_pkcs7_t pkcs7, + gnutls_x509_crl_t crl); +int gnutls_pkcs7_delete_crl(gnutls_pkcs7_t pkcs7, + int indx); + +/* X.509 Certificate verification functions. + */ +typedef enum gnutls_certificate_verify_flags + { + /* If set a signer does not have to be a certificate authority. This + * flag should normaly be disabled, unless you know what this means. + */ + GNUTLS_VERIFY_DISABLE_CA_SIGN = 1, + + /* Allow only trusted CA certificates that have version 1. This is + * safer than GNUTLS_VERIFY_ALLOW_ANY_X509_V1_CA_CRT, and should be + * used instead. That way only signers in your trusted list will be + * allowed to have certificates of version 1. + */ + GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT = 2, + + /* If a certificate is not signed by anyone trusted but exists in + * the trusted CA list do not treat it as trusted. + */ + GNUTLS_VERIFY_DO_NOT_ALLOW_SAME = 4, + + /* Allow CA certificates that have version 1 (both root and + * intermediate). This might be dangerous since those haven't the + * basicConstraints extension. Must be used in combination with + * GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT. + */ + GNUTLS_VERIFY_ALLOW_ANY_X509_V1_CA_CRT = 8, + + /* Allow certificates to be signed using the broken MD2 algorithm. + */ + GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD2 = 16, + + /* Allow certificates to be signed using the broken MD5 algorithm. + */ + GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5 = 32 + } gnutls_certificate_verify_flags; + +int gnutls_x509_crt_check_issuer(gnutls_x509_crt_t cert, + gnutls_x509_crt_t issuer); + +int gnutls_x509_crt_list_verify(const gnutls_x509_crt_t * cert_list, + int cert_list_length, + const gnutls_x509_crt_t * CA_list, + int CA_list_length, + const gnutls_x509_crl_t * CRL_list, + int CRL_list_length, + unsigned int flags, + unsigned int *verify); + +int gnutls_x509_crt_verify(gnutls_x509_crt_t cert, + const gnutls_x509_crt_t * CA_list, + int CA_list_length, + unsigned int flags, + unsigned int *verify); +int gnutls_x509_crl_verify(gnutls_x509_crl_t crl, + const gnutls_x509_crt_t * CA_list, + int CA_list_length, + unsigned int flags, + unsigned int *verify); + +int gnutls_x509_crt_check_revocation(gnutls_x509_crt_t cert, + const gnutls_x509_crl_t * + crl_list, + int crl_list_length); + +int gnutls_x509_crt_get_fingerprint(gnutls_x509_crt_t cert, + gnutls_digest_algorithm_t algo, + void *buf, + size_t * sizeof_buf); + +int gnutls_x509_crt_get_key_purpose_oid(gnutls_x509_crt_t cert, + int indx, + void *oid, + size_t * sizeof_oid, + unsigned int *critical); +int gnutls_x509_crt_set_key_purpose_oid(gnutls_x509_crt_t cert, + const void *oid, + unsigned int critical); + +/* Private key handling. + */ + +/* Flags for the gnutls_x509_privkey_export_pkcs8() function. + */ +typedef enum gnutls_pkcs_encrypt_flags_t + { + GNUTLS_PKCS_PLAIN = 1, /* if set the private key will not + * be encrypted. + */ + GNUTLS_PKCS_USE_PKCS12_3DES = 2, + GNUTLS_PKCS_USE_PKCS12_ARCFOUR = 4, + GNUTLS_PKCS_USE_PKCS12_RC2_40 = 8, + GNUTLS_PKCS_USE_PBES2_3DES = 16 + } gnutls_pkcs_encrypt_flags_t; + +#define GNUTLS_PKCS8_PLAIN GNUTLS_PKCS_PLAIN +#define GNUTLS_PKCS8_USE_PKCS12_3DES GNUTLS_PKCS_USE_PKCS12_3DES +#define GNUTLS_PKCS8_USE_PKCS12_ARCFOUR GNUTLS_PKCS_USE_PKCS12_ARCFOUR +#define GNUTLS_PKCS8_USE_PKCS12_RC2_40 GNUTLS_PKCS_USE_PKCS12_RC2_40 + +int gnutls_x509_privkey_init(gnutls_x509_privkey_t * key); +void gnutls_x509_privkey_deinit(gnutls_x509_privkey_t key); +int gnutls_x509_privkey_cpy(gnutls_x509_privkey_t dst, + gnutls_x509_privkey_t src); +int gnutls_x509_privkey_import(gnutls_x509_privkey_t key, + const gnutls_datum_t * data, + gnutls_x509_crt_fmt_t format); +int gnutls_x509_privkey_import_pkcs8(gnutls_x509_privkey_t key, + const gnutls_datum_t * data, + gnutls_x509_crt_fmt_t format, + const char *pass, + unsigned int flags); +int gnutls_x509_privkey_import_rsa_raw(gnutls_x509_privkey_t key, + const gnutls_datum_t * m, + const gnutls_datum_t * e, + const gnutls_datum_t * d, + const gnutls_datum_t * p, + const gnutls_datum_t * q, + const gnutls_datum_t * u); +int gnutls_x509_privkey_fix(gnutls_x509_privkey_t key); + +int gnutls_x509_privkey_export_dsa_raw(gnutls_x509_privkey_t key, + gnutls_datum_t * p, + gnutls_datum_t * q, + gnutls_datum_t * g, + gnutls_datum_t * y, + gnutls_datum_t * x); +int gnutls_x509_privkey_import_dsa_raw(gnutls_x509_privkey_t key, + const gnutls_datum_t * p, + const gnutls_datum_t * q, + const gnutls_datum_t * g, + const gnutls_datum_t * y, + const gnutls_datum_t * x); + +int gnutls_x509_privkey_get_pk_algorithm(gnutls_x509_privkey_t key); +int gnutls_x509_privkey_get_key_id(gnutls_x509_privkey_t key, + unsigned int flags, + unsigned char *output_data, + size_t * output_data_size); + +int gnutls_x509_privkey_generate(gnutls_x509_privkey_t key, + gnutls_pk_algorithm_t algo, + unsigned int bits, + unsigned int flags); + +int gnutls_x509_privkey_export(gnutls_x509_privkey_t key, + gnutls_x509_crt_fmt_t format, + void *output_data, + size_t * output_data_size); +int gnutls_x509_privkey_export_pkcs8(gnutls_x509_privkey_t key, + gnutls_x509_crt_fmt_t format, + const char *password, + unsigned int flags, + void *output_data, + size_t * output_data_size); +int gnutls_x509_privkey_export_rsa_raw(gnutls_x509_privkey_t key, + gnutls_datum_t * m, + gnutls_datum_t * e, + gnutls_datum_t * d, + gnutls_datum_t * p, + gnutls_datum_t * q, + gnutls_datum_t * u); + +/* Signing stuff. + */ +int gnutls_x509_privkey_sign_data(gnutls_x509_privkey_t key, + gnutls_digest_algorithm_t digest, + unsigned int flags, + const gnutls_datum_t * data, + void *signature, + size_t * signature_size); +int gnutls_x509_privkey_verify_data(gnutls_x509_privkey_t key, + unsigned int flags, + const gnutls_datum_t * data, + const gnutls_datum_t * signature); +int gnutls_x509_crt_verify_data(gnutls_x509_crt_t crt, + unsigned int flags, + const gnutls_datum_t * data, + const gnutls_datum_t * signature); + +int gnutls_x509_privkey_sign_hash(gnutls_x509_privkey_t key, + const gnutls_datum_t * hash, + gnutls_datum_t * signature); + +/* Certificate request stuff. + */ +struct gnutls_x509_crq_int; +typedef struct gnutls_x509_crq_int *gnutls_x509_crq_t; + +int gnutls_x509_crq_init(gnutls_x509_crq_t * crq); +void gnutls_x509_crq_deinit(gnutls_x509_crq_t crq); +int gnutls_x509_crq_import(gnutls_x509_crq_t crq, + const gnutls_datum_t * data, + gnutls_x509_crt_fmt_t format); +int gnutls_x509_crq_get_pk_algorithm(gnutls_x509_crq_t crq, + unsigned int *bits); +int gnutls_x509_crq_get_dn(gnutls_x509_crq_t crq, + char *buf, + size_t * sizeof_buf); +int gnutls_x509_crq_get_dn_oid(gnutls_x509_crq_t crq, + int indx, + void *oid, + size_t * sizeof_oid); +int gnutls_x509_crq_get_dn_by_oid(gnutls_x509_crq_t crq, + const char *oid, + int indx, + unsigned int raw_flag, + void *buf, + size_t * sizeof_buf); +int gnutls_x509_crq_set_dn_by_oid(gnutls_x509_crq_t crq, + const char *oid, + unsigned int raw_flag, + const void *name, + unsigned int sizeof_name); +int gnutls_x509_crq_set_version(gnutls_x509_crq_t crq, + unsigned int version); +int gnutls_x509_crq_set_key(gnutls_x509_crq_t crq, + gnutls_x509_privkey_t key); +int gnutls_x509_crq_sign2(gnutls_x509_crq_t crq, + gnutls_x509_privkey_t key, + gnutls_digest_algorithm_t, + unsigned int flags); +int gnutls_x509_crq_sign(gnutls_x509_crq_t crq, + gnutls_x509_privkey_t key); + +int gnutls_x509_crq_set_challenge_password(gnutls_x509_crq_t crq, + const char *pass); +int gnutls_x509_crq_get_challenge_password(gnutls_x509_crq_t crq, + char *pass, + size_t * sizeof_pass); + +int gnutls_x509_crq_set_attribute_by_oid(gnutls_x509_crq_t crq, + const char *oid, + void *buf, + size_t sizeof_buf); +int gnutls_x509_crq_get_attribute_by_oid(gnutls_x509_crq_t crq, + const char *oid, + int indx, + void *buf, + size_t * sizeof_buf); + +int gnutls_x509_crq_export(gnutls_x509_crq_t crq, + gnutls_x509_crt_fmt_t format, + void *output_data, + size_t * output_data_size); + +int gnutls_x509_crt_set_crq(gnutls_x509_crt_t crt, + gnutls_x509_crq_t crq); + +#ifdef __cplusplus +} +#endif + +#define HASH_OID_SHA1 "1.3.14.3.2.26" +#define HASH_OID_MD5 "1.2.840.113549.2.5" +#define HASH_OID_MD2 "1.2.840.113549.2.2" +#define HASH_OID_RMD160 "1.3.36.3.2.1" +#define HASH_OID_SHA256 "2.16.840.1.101.3.4.2.1" +#define HASH_OID_SHA384 "2.16.840.1.101.3.4.2.2" +#define HASH_OID_SHA512 "2.16.840.1.101.3.4.2.3" + +typedef struct gnutls_x509_crl_int + { + ASN1_TYPE crl; + } gnutls_x509_crl_int; + +typedef struct gnutls_x509_crt_int + { + ASN1_TYPE cert; + int use_extensions; + } gnutls_x509_crt_int; + +#define MAX_PRIV_PARAMS_SIZE 6 /* ok for RSA and DSA */ + +/* parameters should not be larger than this limit */ +#define DSA_PRIVATE_PARAMS 5 +#define DSA_PUBLIC_PARAMS 4 +#define RSA_PRIVATE_PARAMS 6 +#define RSA_PUBLIC_PARAMS 2 + +#if MAX_PRIV_PARAMS_SIZE - RSA_PRIVATE_PARAMS < 0 +# error INCREASE MAX_PRIV_PARAMS +#endif + +#if MAX_PRIV_PARAMS_SIZE - DSA_PRIVATE_PARAMS < 0 +# error INCREASE MAX_PRIV_PARAMS +#endif + +typedef struct gnutls_x509_privkey_int + { + mpi_t params[MAX_PRIV_PARAMS_SIZE]; /* the size of params depends on the public + * key algorithm + */ + /* + * RSA: [0] is modulus + * [1] is public exponent + * [2] is private exponent + * [3] is prime1 (p) + * [4] is prime2 (q) + * [5] is coefficient (u == inverse of p mod q) + * note that other packages used inverse of q mod p, + * so we need to perform conversions. + * DSA: [0] is p + * [1] is q + * [2] is g + * [3] is y (public key) + * [4] is x (private key) + */ + int params_size; /* holds the number of params */ + + gnutls_pk_algorithm_t pk_algorithm; + + int crippled; /* The crippled keys will not use the ASN1_TYPE key. + * The encoding will only be performed at the export + * phase, to optimize copying etc. Cannot be used with + * the exported API (used internally only). + */ + ASN1_TYPE key; + } gnutls_x509_privkey_int; + +int gnutls_x509_crt_get_issuer_dn_by_oid(gnutls_x509_crt_t cert, + const char *oid, + int indx, + unsigned int raw_flag, + void *buf, + size_t * sizeof_buf); +int gnutls_x509_crt_get_subject_alt_name(gnutls_x509_crt_t cert, + unsigned int seq, + void *ret, + size_t * ret_size, + unsigned int *critical); +int gnutls_x509_crt_get_dn_by_oid(gnutls_x509_crt_t cert, + const char *oid, + int indx, + unsigned int raw_flag, + void *buf, + size_t * sizeof_buf); +int gnutls_x509_crt_get_ca_status(gnutls_x509_crt_t cert, + unsigned int *critical); +int gnutls_x509_crt_get_pk_algorithm(gnutls_x509_crt_t cert, + unsigned int *bits); + +int _gnutls_x509_crt_cpy(gnutls_x509_crt_t dest, + gnutls_x509_crt_t src); + +int gnutls_x509_crt_get_serial(gnutls_x509_crt_t cert, + void *result, + size_t * result_size); + +int _gnutls_x509_compare_raw_dn(const gnutls_datum_t * dn1, + const gnutls_datum_t * dn2); + +int gnutls_x509_crt_check_revocation(gnutls_x509_crt_t cert, + const gnutls_x509_crl_t * crl_list, + int crl_list_length); + +int _gnutls_x509_crl_cpy(gnutls_x509_crl_t dest, + gnutls_x509_crl_t src); +int _gnutls_x509_crl_get_raw_issuer_dn(gnutls_x509_crl_t crl, + gnutls_datum_t * dn); +int gnutls_x509_crl_get_crt_count(gnutls_x509_crl_t crl); +int gnutls_x509_crl_get_crt_serial(gnutls_x509_crl_t crl, + int indx, + unsigned char *serial, + size_t * serial_size, + time_t * t); + +void gnutls_x509_crl_deinit(gnutls_x509_crl_t crl); +int gnutls_x509_crl_init(gnutls_x509_crl_t * crl); +int gnutls_x509_crl_import(gnutls_x509_crl_t crl, + const gnutls_datum_t * data, + gnutls_x509_crt_fmt_t format); +int gnutls_x509_crl_export(gnutls_x509_crl_t crl, + gnutls_x509_crt_fmt_t format, + void *output_data, + size_t * output_data_size); + +int gnutls_x509_crt_init(gnutls_x509_crt_t * cert); +void gnutls_x509_crt_deinit(gnutls_x509_crt_t cert); +int gnutls_x509_crt_import(gnutls_x509_crt_t cert, + const gnutls_datum_t * data, + gnutls_x509_crt_fmt_t format); +int gnutls_x509_crt_export(gnutls_x509_crt_t cert, + gnutls_x509_crt_fmt_t format, + void *output_data, + size_t * output_data_size); + +int gnutls_x509_crt_get_key_usage(gnutls_x509_crt_t cert, + unsigned int *key_usage, + unsigned int *critical); +int gnutls_x509_crt_get_signature_algorithm(gnutls_x509_crt_t cert); +int gnutls_x509_crt_get_version(gnutls_x509_crt_t cert); + +int gnutls_x509_privkey_init(gnutls_x509_privkey_t * key); +void gnutls_x509_privkey_deinit(gnutls_x509_privkey_t key); + +int gnutls_x509_privkey_generate(gnutls_x509_privkey_t key, + gnutls_pk_algorithm_t algo, + unsigned int bits, + unsigned int flags); + +int gnutls_x509_privkey_import(gnutls_x509_privkey_t key, + const gnutls_datum_t * data, + gnutls_x509_crt_fmt_t format); +int gnutls_x509_privkey_get_pk_algorithm(gnutls_x509_privkey_t key); +int gnutls_x509_privkey_import_rsa_raw(gnutls_x509_privkey_t key, + const gnutls_datum_t * m, + const gnutls_datum_t * e, + const gnutls_datum_t * d, + const gnutls_datum_t * p, + const gnutls_datum_t * q, + const gnutls_datum_t * u); +int gnutls_x509_privkey_export_rsa_raw(gnutls_x509_privkey_t key, + gnutls_datum_t * m, + gnutls_datum_t * e, + gnutls_datum_t * d, + gnutls_datum_t * p, + gnutls_datum_t * q, + gnutls_datum_t * u); +int gnutls_x509_privkey_export(gnutls_x509_privkey_t key, + gnutls_x509_crt_fmt_t format, + void *output_data, + size_t * output_data_size); + +#define GNUTLS_CRL_REASON_UNUSED 128 +#define GNUTLS_CRL_REASON_KEY_COMPROMISE 64 +#define GNUTLS_CRL_REASON_CA_COMPROMISE 32 +#define GNUTLS_CRL_REASON_AFFILIATION_CHANGED 16 +#define GNUTLS_CRL_REASON_SUPERSEEDED 8 +#define GNUTLS_CRL_REASON_CESSATION_OF_OPERATION 4 +#define GNUTLS_CRL_REASON_CERTIFICATE_HOLD 2 +#define GNUTLS_CRL_REASON_PRIVILEGE_WITHDRAWN 1 +#define GNUTLS_CRL_REASON_AA_COMPROMISE 32768 + +#endif diff --git a/src/daemon/https/x509/x509_privkey.c b/src/daemon/https/x509/x509_privkey.c new file mode 100644 index 00000000..f5c64dee --- /dev/null +++ b/src/daemon/https/x509/x509_privkey.c @@ -0,0 +1,1509 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2007 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int _gnutls_asn1_encode_rsa (ASN1_TYPE * c2, mpi_t * params); +int _gnutls_asn1_encode_dsa (ASN1_TYPE * c2, mpi_t * params); + +/* remove this when libgcrypt can handle the PKCS #1 coefficients from + * rsa keys + */ +#define CALC_COEFF 1 + +/** + * gnutls_x509_privkey_init - This function initializes a gnutls_crl structure + * @key: The structure to be initialized + * + * This function will initialize an private key structure. + * + * Returns 0 on success. + * + **/ +int +gnutls_x509_privkey_init (gnutls_x509_privkey_t * key) +{ + *key = gnutls_calloc (1, sizeof (gnutls_x509_privkey_int)); + + if (*key) + { + (*key)->key = ASN1_TYPE_EMPTY; + (*key)->pk_algorithm = GNUTLS_PK_UNKNOWN; + return 0; /* success */ + } + + return GNUTLS_E_MEMORY_ERROR; +} + +/** + * gnutls_x509_privkey_deinit - This function deinitializes memory used by a gnutls_x509_privkey_t structure + * @key: The structure to be initialized + * + * This function will deinitialize a private key structure. + * + **/ +void +gnutls_x509_privkey_deinit (gnutls_x509_privkey_t key) +{ + int i; + + if (!key) + return; + + for (i = 0; i < key->params_size; i++) + { + _gnutls_mpi_release (&key->params[i]); + } + + asn1_delete_structure (&key->key); + gnutls_free (key); +} + +/** + * gnutls_x509_privkey_cpy - This function copies a private key + * @dst: The destination key, which should be initialized. + * @src: The source key + * + * This function will copy a private key from source to destination key. + * + **/ +int +gnutls_x509_privkey_cpy (gnutls_x509_privkey_t dst, gnutls_x509_privkey_t src) +{ + int i, ret; + + if (!src || !dst) + return GNUTLS_E_INVALID_REQUEST; + + for (i = 0; i < src->params_size; i++) + { + dst->params[i] = _gnutls_mpi_copy (src->params[i]); + if (dst->params[i] == NULL) + return GNUTLS_E_MEMORY_ERROR; + } + + dst->params_size = src->params_size; + dst->pk_algorithm = src->pk_algorithm; + dst->crippled = src->crippled; + + if (!src->crippled) + { + switch (dst->pk_algorithm) + { + case GNUTLS_PK_RSA: + ret = _gnutls_asn1_encode_rsa (&dst->key, dst->params); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + break; + default: + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + } + + return 0; +} + +/* Converts an RSA PKCS#1 key to + * an internal structure (gnutls_private_key) + */ +ASN1_TYPE +_gnutls_privkey_decode_pkcs1_rsa_key (const gnutls_datum_t * raw_key, + gnutls_x509_privkey_t pkey) +{ + int result; + ASN1_TYPE pkey_asn; + + if ((result = asn1_create_element (_gnutls_get_gnutls_asn (), + "GNUTLS.RSAPrivateKey", + &pkey_asn)) != ASN1_SUCCESS) + { + gnutls_assert (); + return NULL; + } + + if ((sizeof (pkey->params) / sizeof (mpi_t)) < RSA_PRIVATE_PARAMS) + { + gnutls_assert (); + /* internal error. Increase the mpi_ts in params */ + return NULL; + } + + result = asn1_der_decoding (&pkey_asn, raw_key->data, raw_key->size, NULL); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + goto error; + } + + if ((result = _gnutls_x509_read_int (pkey_asn, "modulus", &pkey->params[0])) + < 0) + { + gnutls_assert (); + goto error; + } + + if ((result = _gnutls_x509_read_int (pkey_asn, "publicExponent", + &pkey->params[1])) < 0) + { + gnutls_assert (); + goto error; + } + + if ((result = _gnutls_x509_read_int (pkey_asn, "privateExponent", + &pkey->params[2])) < 0) + { + gnutls_assert (); + goto error; + } + + if ((result = _gnutls_x509_read_int (pkey_asn, "prime1", &pkey->params[3])) + < 0) + { + gnutls_assert (); + goto error; + } + + if ((result = _gnutls_x509_read_int (pkey_asn, "prime2", &pkey->params[4])) + < 0) + { + gnutls_assert (); + goto error; + } + +#ifdef CALC_COEFF + /* Calculate the coefficient. This is because the gcrypt + * library is uses the p,q in the reverse order. + */ + pkey->params[5] = + _gnutls_mpi_snew (_gnutls_mpi_get_nbits (pkey->params[0])); + + if (pkey->params[5] == NULL) + { + gnutls_assert (); + goto error; + } + + _gnutls_mpi_invm (pkey->params[5], pkey->params[3], pkey->params[4]); + /* p, q */ +#else + if ((result = _gnutls_x509_read_int (pkey_asn, "coefficient", + &pkey->params[5])) < 0) + { + gnutls_assert (); + goto error; + } +#endif + pkey->params_size = 6; + + return pkey_asn; + +error:asn1_delete_structure (&pkey_asn); + _gnutls_mpi_release (&pkey->params[0]); + _gnutls_mpi_release (&pkey->params[1]); + _gnutls_mpi_release (&pkey->params[2]); + _gnutls_mpi_release (&pkey->params[3]); + _gnutls_mpi_release (&pkey->params[4]); + _gnutls_mpi_release (&pkey->params[5]); + return NULL; + +} + +static ASN1_TYPE +decode_dsa_key (const gnutls_datum_t * raw_key, gnutls_x509_privkey_t pkey) +{ + int result; + ASN1_TYPE dsa_asn; + + if ((result = asn1_create_element (_gnutls_get_gnutls_asn (), + "GNUTLS.DSAPrivateKey", + &dsa_asn)) != ASN1_SUCCESS) + { + gnutls_assert (); + return NULL; + } + + if ((sizeof (pkey->params) / sizeof (mpi_t)) < DSA_PRIVATE_PARAMS) + { + gnutls_assert (); + /* internal error. Increase the mpi_ts in params */ + return NULL; + } + + result = asn1_der_decoding (&dsa_asn, raw_key->data, raw_key->size, NULL); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + goto error; + } + + if ((result = _gnutls_x509_read_int (dsa_asn, "p", &pkey->params[0])) < 0) + { + gnutls_assert (); + goto error; + } + + if ((result = _gnutls_x509_read_int (dsa_asn, "q", &pkey->params[1])) < 0) + { + gnutls_assert (); + goto error; + } + + if ((result = _gnutls_x509_read_int (dsa_asn, "g", &pkey->params[2])) < 0) + { + gnutls_assert (); + goto error; + } + + if ((result = _gnutls_x509_read_int (dsa_asn, "Y", &pkey->params[3])) < 0) + { + gnutls_assert (); + goto error; + } + + if ((result = + _gnutls_x509_read_int (dsa_asn, "priv", &pkey->params[4])) < 0) + { + gnutls_assert (); + goto error; + } + pkey->params_size = 5; + + return dsa_asn; + +error:asn1_delete_structure (&dsa_asn); + _gnutls_mpi_release (&pkey->params[0]); + _gnutls_mpi_release (&pkey->params[1]); + _gnutls_mpi_release (&pkey->params[2]); + _gnutls_mpi_release (&pkey->params[3]); + _gnutls_mpi_release (&pkey->params[4]); + return NULL; + +} + +#define PEM_KEY_DSA "DSA PRIVATE KEY" +#define PEM_KEY_RSA "RSA PRIVATE KEY" + +/** + * gnutls_x509_privkey_import - This function will import a DER or PEM encoded key + * @key: The structure to store the parsed key + * @data: The DER or PEM encoded certificate. + * @format: One of DER or PEM + * + * This function will convert the given DER or PEM encoded key + * to the native gnutls_x509_privkey_t format. The output will be stored in @key . + * + * If the key is PEM encoded it should have a header of "RSA PRIVATE KEY", or + * "DSA PRIVATE KEY". + * + * Returns 0 on success. + * + **/ +int +gnutls_x509_privkey_import (gnutls_x509_privkey_t key, + const gnutls_datum_t * data, + gnutls_x509_crt_fmt_t format) +{ + int result = 0, need_free = 0; + gnutls_datum_t _data; + + if (key == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + _data.data = data->data; + _data.size = data->size; + + key->pk_algorithm = GNUTLS_PK_UNKNOWN; + + /* If the Certificate is in PEM format then decode it + */ + if (format == GNUTLS_X509_FMT_PEM) + { + opaque *out; + + /* Try the first header */ + result + = _gnutls_fbase64_decode (PEM_KEY_RSA, data->data, data->size, &out); + key->pk_algorithm = GNUTLS_PK_RSA; + + // TODO rm +// if (result == GNUTLS_E_BASE64_UNEXPECTED_HEADER_ERROR) +// { +// /* try for the second header */ +// result = _gnutls_fbase64_decode(PEM_KEY_DSA, data->data, data->size, +// &out); +// key->pk_algorithm = GNUTLS_PK_DSA; +// +// if (result <= 0) +// { +// if (result == 0) +// result = GNUTLS_E_INTERNAL_ERROR; +// gnutls_assert (); +// return result; +// } +// } + + _data.data = out; + _data.size = result; + + need_free = 1; + } + + if (key->pk_algorithm == GNUTLS_PK_RSA) + { + key->key = _gnutls_privkey_decode_pkcs1_rsa_key (&_data, key); + if (key->key == NULL) + gnutls_assert (); + } + else + { + /* Try decoding with both, and accept the one that + * succeeds. + */ + key->pk_algorithm = GNUTLS_PK_RSA; + key->key = _gnutls_privkey_decode_pkcs1_rsa_key (&_data, key); + + // TODO rm +// if (key->key == NULL) +// { +// key->pk_algorithm = GNUTLS_PK_DSA; +// key->key = decode_dsa_key(&_data, key); +// if (key->key == NULL) +// gnutls_assert(); +// } + } + + if (key->key == NULL) + { + gnutls_assert (); + result = GNUTLS_E_ASN1_DER_ERROR; + goto cleanup; + } + + if (need_free) + _gnutls_free_datum (&_data); + + /* The key has now been decoded. + */ + + return 0; + +cleanup:key->pk_algorithm = GNUTLS_PK_UNKNOWN; + if (need_free) + _gnutls_free_datum (&_data); + return result; +} + +#define FREE_RSA_PRIVATE_PARAMS for (i=0;iparams[i]) +#define FREE_DSA_PRIVATE_PARAMS for (i=0;iparams[i]) + +/** + * gnutls_x509_privkey_import_rsa_raw - This function will import a raw RSA key + * @key: The structure to store the parsed key + * @m: holds the modulus + * @e: holds the public exponent + * @d: holds the private exponent + * @p: holds the first prime (p) + * @q: holds the second prime (q) + * @u: holds the coefficient + * + * This function will convert the given RSA raw parameters + * to the native gnutls_x509_privkey_t format. The output will be stored in @key. + * + **/ +int +gnutls_x509_privkey_import_rsa_raw (gnutls_x509_privkey_t key, + const gnutls_datum_t * m, + const gnutls_datum_t * e, + const gnutls_datum_t * d, + const gnutls_datum_t * p, + const gnutls_datum_t * q, + const gnutls_datum_t * u) +{ + int i = 0, ret; + size_t siz = 0; + + if (key == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + siz = m->size; + if (_gnutls_mpi_scan_nz (&key->params[0], m->data, &siz)) + { + gnutls_assert (); + FREE_RSA_PRIVATE_PARAMS; + return GNUTLS_E_MPI_SCAN_FAILED; + } + + siz = e->size; + if (_gnutls_mpi_scan_nz (&key->params[1], e->data, &siz)) + { + gnutls_assert (); + FREE_RSA_PRIVATE_PARAMS; + return GNUTLS_E_MPI_SCAN_FAILED; + } + + siz = d->size; + if (_gnutls_mpi_scan_nz (&key->params[2], d->data, &siz)) + { + gnutls_assert (); + FREE_RSA_PRIVATE_PARAMS; + return GNUTLS_E_MPI_SCAN_FAILED; + } + + siz = p->size; + if (_gnutls_mpi_scan_nz (&key->params[3], p->data, &siz)) + { + gnutls_assert (); + FREE_RSA_PRIVATE_PARAMS; + return GNUTLS_E_MPI_SCAN_FAILED; + } + + siz = q->size; + if (_gnutls_mpi_scan_nz (&key->params[4], q->data, &siz)) + { + gnutls_assert (); + FREE_RSA_PRIVATE_PARAMS; + return GNUTLS_E_MPI_SCAN_FAILED; + } + +#ifdef CALC_COEFF + key->params[5] = _gnutls_mpi_snew (_gnutls_mpi_get_nbits (key->params[0])); + + if (key->params[5] == NULL) + { + gnutls_assert (); + FREE_RSA_PRIVATE_PARAMS; + return GNUTLS_E_MEMORY_ERROR; + } + + _gnutls_mpi_invm (key->params[5], key->params[3], key->params[4]); +#else + siz = u->size; + if (_gnutls_mpi_scan_nz (&key->params[5], u->data, &siz)) + { + gnutls_assert (); + FREE_RSA_PRIVATE_PARAMS; + return GNUTLS_E_MPI_SCAN_FAILED; + } +#endif + + if (!key->crippled) + { + ret = _gnutls_asn1_encode_rsa (&key->key, key->params); + if (ret < 0) + { + gnutls_assert (); + FREE_RSA_PRIVATE_PARAMS; + return ret; + } + } + + key->params_size = RSA_PRIVATE_PARAMS; + key->pk_algorithm = GNUTLS_PK_RSA; + + return 0; + +} + +/** + * gnutls_x509_privkey_get_pk_algorithm - This function returns the key's PublicKey algorithm + * @key: should contain a gnutls_x509_privkey_t structure + * + * This function will return the public key algorithm of a private + * key. + * + * Returns a member of the gnutls_pk_algorithm_t enumeration on success, + * or a negative value on error. + * + **/ +int +gnutls_x509_privkey_get_pk_algorithm (gnutls_x509_privkey_t key) +{ + if (key == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + return key->pk_algorithm; +} + +/** + * gnutls_x509_privkey_export - This function will export the private key + * @key: Holds the key + * @format: the format of output params. One of PEM or DER. + * @output_data: will contain a private key PEM or DER encoded + * @output_data_size: holds the size of output_data (and will be + * replaced by the actual size of parameters) + * + * This function will export the private key to a PKCS1 structure for + * RSA keys, or an integer sequence for DSA keys. The DSA keys are in + * the same format with the parameters used by openssl. + * + * If the buffer provided is not long enough to hold the output, then + * *output_data_size is updated and GNUTLS_E_SHORT_MEMORY_BUFFER will + * be returned. + * + * If the structure is PEM encoded, it will have a header + * of "BEGIN RSA PRIVATE KEY". + * + * Return value: In case of failure a negative value will be + * returned, and 0 on success. + * + **/ +int +gnutls_x509_privkey_export (gnutls_x509_privkey_t key, + gnutls_x509_crt_fmt_t format, + void *output_data, size_t * output_data_size) +{ + char *msg; + int ret; + + if (key == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + if (key->pk_algorithm == GNUTLS_PK_RSA) + msg = PEM_KEY_RSA; + else + msg = NULL; + + if (key->crippled) + { /* encode the parameters on the fly. + */ + switch (key->pk_algorithm) + { + case GNUTLS_PK_RSA: + ret = _gnutls_asn1_encode_rsa (&key->key, key->params); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + break; + default: + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + } + + return _gnutls_x509_export_int (key->key, format, msg, output_data, + output_data_size); +} + +/** + * gnutls_x509_privkey_export_rsa_raw - This function will export the RSA private key + * @key: a structure that holds the rsa parameters + * @m: will hold the modulus + * @e: will hold the public exponent + * @d: will hold the private exponent + * @p: will hold the first prime (p) + * @q: will hold the second prime (q) + * @u: will hold the coefficient + * + * This function will export the RSA private key's parameters found in the given + * structure. The new parameters will be allocated using + * gnutls_malloc() and will be stored in the appropriate datum. + * + **/ +int +gnutls_x509_privkey_export_rsa_raw (gnutls_x509_privkey_t key, + gnutls_datum_t * m, + gnutls_datum_t * e, + gnutls_datum_t * d, + gnutls_datum_t * p, + gnutls_datum_t * q, gnutls_datum_t * u) +{ + int ret; + mpi_t coeff = NULL; + + if (key == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + m->data = e->data = d->data = p->data = q->data = u->data = NULL; + m->size = e->size = d->size = p->size = q->size = u->size = 0; + + ret = _gnutls_mpi_dprint (m, key->params[0]); + if (ret < 0) + { + gnutls_assert (); + goto error; + } + + /* E */ + ret = _gnutls_mpi_dprint (e, key->params[1]); + if (ret < 0) + { + gnutls_assert (); + goto error; + } + + /* D */ + ret = _gnutls_mpi_dprint (d, key->params[2]); + if (ret < 0) + { + gnutls_assert (); + goto error; + } + + /* P */ + ret = _gnutls_mpi_dprint (p, key->params[3]); + if (ret < 0) + { + gnutls_assert (); + goto error; + } + + /* Q */ + ret = _gnutls_mpi_dprint (q, key->params[4]); + if (ret < 0) + { + gnutls_assert (); + goto error; + } + +#ifdef CALC_COEFF + coeff = _gnutls_mpi_snew (_gnutls_mpi_get_nbits (key->params[0])); + + if (coeff == NULL) + { + gnutls_assert (); + ret = GNUTLS_E_MEMORY_ERROR; + goto error; + } + + _gnutls_mpi_invm (coeff, key->params[4], key->params[3]); + ret = _gnutls_mpi_dprint (u, coeff); + if (ret < 0) + { + gnutls_assert (); + goto error; + } + + _gnutls_mpi_release (&coeff); +#else + /* U */ + ret = _gnutls_mpi_dprint (u, key->params[5]); + if (ret < 0) + { + gnutls_assert (); + goto error; + } +#endif + + return 0; + +error:_gnutls_free_datum (m); + _gnutls_free_datum (d); + _gnutls_free_datum (e); + _gnutls_free_datum (p); + _gnutls_free_datum (q); + _gnutls_mpi_release (&coeff); + + return ret; +} + +/** + * gnutls_x509_privkey_export_dsa_raw - This function will export the DSA private key + * @params: a structure that holds the DSA parameters + * @p: will hold the p + * @q: will hold the q + * @g: will hold the g + * @y: will hold the y + * @x: will hold the x + * + * This function will export the DSA private key's parameters found in the given + * structure. The new parameters will be allocated using + * gnutls_malloc() and will be stored in the appropriate datum. + * + **/ +int +gnutls_x509_privkey_export_dsa_raw (gnutls_x509_privkey_t key, + gnutls_datum_t * p, + gnutls_datum_t * q, + gnutls_datum_t * g, + gnutls_datum_t * y, gnutls_datum_t * x) +{ + int ret; + + if (key == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + /* P */ + ret = _gnutls_mpi_dprint (p, key->params[0]); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + /* Q */ + ret = _gnutls_mpi_dprint (q, key->params[1]); + if (ret < 0) + { + gnutls_assert (); + _gnutls_free_datum (p); + return ret; + } + + /* G */ + ret = _gnutls_mpi_dprint (g, key->params[2]); + if (ret < 0) + { + gnutls_assert (); + _gnutls_free_datum (p); + _gnutls_free_datum (q); + return ret; + } + + /* Y */ + ret = _gnutls_mpi_dprint (y, key->params[3]); + if (ret < 0) + { + gnutls_assert (); + _gnutls_free_datum (p); + _gnutls_free_datum (g); + _gnutls_free_datum (q); + return ret; + } + + /* X */ + ret = _gnutls_mpi_dprint (x, key->params[4]); + if (ret < 0) + { + gnutls_assert (); + _gnutls_free_datum (y); + _gnutls_free_datum (p); + _gnutls_free_datum (g); + _gnutls_free_datum (q); + return ret; + } + + return 0; +} + +/* Encodes the RSA parameters into an ASN.1 RSA private key structure. + */ +static int +_gnutls_asn1_encode_rsa (ASN1_TYPE * c2, mpi_t * params) +{ + int result, i; + size_t size[8], total; + opaque *m_data, *pube_data, *prie_data; + opaque *p1_data, *p2_data, *u_data, *exp1_data, *exp2_data; + opaque *all_data = NULL, *p; + mpi_t exp1 = NULL, exp2 = NULL, q1 = NULL, p1 = NULL, u = NULL; + opaque null = '\0'; + + /* Read all the sizes */ + total = 0; + for (i = 0; i < 5; i++) + { + _gnutls_mpi_print_lz (NULL, &size[i], params[i]); + total += size[i]; + } + + /* Now generate exp1 and exp2 + */ + exp1 = _gnutls_mpi_salloc_like (params[0]); /* like modulus */ + if (exp1 == NULL) + { + gnutls_assert (); + result = GNUTLS_E_MEMORY_ERROR; + goto cleanup; + } + + exp2 = _gnutls_mpi_salloc_like (params[0]); + if (exp2 == NULL) + { + gnutls_assert (); + result = GNUTLS_E_MEMORY_ERROR; + goto cleanup; + } + + q1 = _gnutls_mpi_salloc_like (params[4]); + if (q1 == NULL) + { + gnutls_assert (); + result = GNUTLS_E_MEMORY_ERROR; + goto cleanup; + } + + p1 = _gnutls_mpi_salloc_like (params[3]); + if (p1 == NULL) + { + gnutls_assert (); + result = GNUTLS_E_MEMORY_ERROR; + goto cleanup; + } + + u = _gnutls_mpi_salloc_like (params[3]); + if (u == NULL) + { + gnutls_assert (); + result = GNUTLS_E_MEMORY_ERROR; + goto cleanup; + } + + _gnutls_mpi_invm (u, params[4], params[3]); + /* inverse of q mod p */ + _gnutls_mpi_print_lz (NULL, &size[5], u); + total += size[5]; + + _gnutls_mpi_sub_ui (p1, params[3], 1); + _gnutls_mpi_sub_ui (q1, params[4], 1); + + _gnutls_mpi_mod (exp1, params[2], p1); + _gnutls_mpi_mod (exp2, params[2], q1); + + /* calculate exp's size */ + _gnutls_mpi_print_lz (NULL, &size[6], exp1); + total += size[6]; + + _gnutls_mpi_print_lz (NULL, &size[7], exp2); + total += size[7]; + + /* Encoding phase. + * allocate data enough to hold everything + */ + all_data = gnutls_secure_malloc (total); + if (all_data == NULL) + { + gnutls_assert (); + result = GNUTLS_E_MEMORY_ERROR; + goto cleanup; + } + + p = all_data; + m_data = p; + p += size[0]; + pube_data = p; + p += size[1]; + prie_data = p; + p += size[2]; + p1_data = p; + p += size[3]; + p2_data = p; + p += size[4]; + u_data = p; + p += size[5]; + exp1_data = p; + p += size[6]; + exp2_data = p; + + _gnutls_mpi_print_lz (m_data, &size[0], params[0]); + _gnutls_mpi_print_lz (pube_data, &size[1], params[1]); + _gnutls_mpi_print_lz (prie_data, &size[2], params[2]); + _gnutls_mpi_print_lz (p1_data, &size[3], params[3]); + _gnutls_mpi_print_lz (p2_data, &size[4], params[4]); + _gnutls_mpi_print_lz (u_data, &size[5], u); + _gnutls_mpi_print_lz (exp1_data, &size[6], exp1); + _gnutls_mpi_print_lz (exp2_data, &size[7], exp2); + + /* Ok. Now we have the data. Create the asn1 structures + */ + + if ((result = + asn1_create_element (_gnutls_get_gnutls_asn (), "GNUTLS.RSAPrivateKey", + c2)) != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + /* Write PRIME + */ + if ((result = asn1_write_value (*c2, "modulus", m_data, size[0])) + != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + if ((result = asn1_write_value (*c2, "publicExponent", pube_data, size[1])) + != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + if ((result = asn1_write_value (*c2, "privateExponent", prie_data, size[2])) + != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + if ((result = asn1_write_value (*c2, "prime1", p1_data, size[3])) + != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + if ((result = asn1_write_value (*c2, "prime2", p2_data, size[4])) + != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + if ((result = asn1_write_value (*c2, "exponent1", exp1_data, size[6])) + != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + if ((result = asn1_write_value (*c2, "exponent2", exp2_data, size[7])) + != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + if ((result = asn1_write_value (*c2, "coefficient", u_data, size[5])) + != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + _gnutls_mpi_release (&exp1); + _gnutls_mpi_release (&exp2); + _gnutls_mpi_release (&q1); + _gnutls_mpi_release (&p1); + _gnutls_mpi_release (&u); + gnutls_free (all_data); + + if ((result = asn1_write_value (*c2, "otherPrimeInfos", + NULL, 0)) != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + if ((result = asn1_write_value (*c2, "version", &null, 1)) != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + return 0; + +cleanup:_gnutls_mpi_release (&u); + _gnutls_mpi_release (&exp1); + _gnutls_mpi_release (&exp2); + _gnutls_mpi_release (&q1); + _gnutls_mpi_release (&p1); + asn1_delete_structure (c2); + gnutls_free (all_data); + + return result; +} + +/* Encodes the DSA parameters into an ASN.1 DSAPrivateKey structure. + */ +int +_gnutls_asn1_encode_dsa (ASN1_TYPE * c2, mpi_t * params) +{ + int result, i; + size_t size[DSA_PRIVATE_PARAMS], total; + opaque *p_data, *q_data, *g_data, *x_data, *y_data; + opaque *all_data = NULL, *p; + opaque null = '\0'; + + /* Read all the sizes */ + total = 0; + for (i = 0; i < DSA_PRIVATE_PARAMS; i++) + { + _gnutls_mpi_print_lz (NULL, &size[i], params[i]); + total += size[i]; + } + + /* Encoding phase. + * allocate data enough to hold everything + */ + all_data = gnutls_secure_malloc (total); + if (all_data == NULL) + { + gnutls_assert (); + result = GNUTLS_E_MEMORY_ERROR; + goto cleanup; + } + + p = all_data; + p_data = p; + p += size[0]; + q_data = p; + p += size[1]; + g_data = p; + p += size[2]; + y_data = p; + p += size[3]; + x_data = p; + + _gnutls_mpi_print_lz (p_data, &size[0], params[0]); + _gnutls_mpi_print_lz (q_data, &size[1], params[1]); + _gnutls_mpi_print_lz (g_data, &size[2], params[2]); + _gnutls_mpi_print_lz (y_data, &size[3], params[3]); + _gnutls_mpi_print_lz (x_data, &size[4], params[4]); + + /* Ok. Now we have the data. Create the asn1 structures + */ + + if ((result = + asn1_create_element (_gnutls_get_gnutls_asn (), "GNUTLS.DSAPrivateKey", + c2)) != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + /* Write PRIME + */ + if ((result = asn1_write_value (*c2, "p", p_data, size[0])) != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + if ((result = asn1_write_value (*c2, "q", q_data, size[1])) != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + if ((result = asn1_write_value (*c2, "g", g_data, size[2])) != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + if ((result = asn1_write_value (*c2, "Y", y_data, size[3])) != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + if ((result = + asn1_write_value (*c2, "priv", x_data, size[4])) != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + gnutls_free (all_data); + + if ((result = asn1_write_value (*c2, "version", &null, 1)) != ASN1_SUCCESS) + { + gnutls_assert (); + result = _gnutls_asn2err (result); + goto cleanup; + } + + return 0; + +cleanup:asn1_delete_structure (c2); + gnutls_free (all_data); + + return result; +} + +/** + * gnutls_x509_privkey_generate - This function will generate a private key + * @key: should contain a gnutls_x509_privkey_t structure + * @algo: is one of RSA or DSA. + * @bits: the size of the modulus + * @flags: unused for now. Must be 0. + * + * This function will generate a random private key. Note that + * this function must be called on an empty private key. + * + * Returns 0 on success or a negative value on error. + * + **/ +int +gnutls_x509_privkey_generate (gnutls_x509_privkey_t key, + gnutls_pk_algorithm_t algo, + unsigned int bits, unsigned int flags) +{ + int ret, params_len; + int i; + + if (key == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + switch (algo) + { + case GNUTLS_PK_RSA: + ret = _gnutls_rsa_generate_params (key->params, ¶ms_len, bits); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + if (!key->crippled) + { + ret = _gnutls_asn1_encode_rsa (&key->key, key->params); + if (ret < 0) + { + gnutls_assert (); + goto cleanup; + } + } + + key->params_size = params_len; + key->pk_algorithm = GNUTLS_PK_RSA; + + break; + default: + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + return 0; + +cleanup:key->pk_algorithm = GNUTLS_PK_UNKNOWN; + key->params_size = 0; + for (i = 0; i < params_len; i++) + _gnutls_mpi_release (&key->params[i]); + + return ret; +} + +/** + * gnutls_x509_privkey_get_key_id - Return unique ID of the key's parameters + * @key: Holds the key + * @flags: should be 0 for now + * @output_data: will contain the key ID + * @output_data_size: holds the size of output_data (and will be + * replaced by the actual size of parameters) + * + * This function will return a unique ID the depends on the public key + * parameters. This ID can be used in checking whether a certificate + * corresponds to the given key. + * + * If the buffer provided is not long enough to hold the output, then + * *output_data_size is updated and GNUTLS_E_SHORT_MEMORY_BUFFER will + * be returned. The output will normally be a SHA-1 hash output, + * which is 20 bytes. + * + * Return value: In case of failure a negative value will be + * returned, and 0 on success. + * + **/ +int +gnutls_x509_privkey_get_key_id (gnutls_x509_privkey_t key, + unsigned int flags, + unsigned char *output_data, + size_t * output_data_size) +{ + int result; + GNUTLS_HASH_HANDLE hd; + gnutls_datum_t der = { NULL, + 0 + }; + + if (key == NULL || key->crippled) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + if (*output_data_size < 20) + { + gnutls_assert (); + *output_data_size = 20; + return GNUTLS_E_SHORT_MEMORY_BUFFER; + } + + if (key->pk_algorithm == GNUTLS_PK_RSA) + { + result = _gnutls_x509_write_rsa_params (key->params, key->params_size, + &der); + if (result < 0) + { + gnutls_assert (); + goto cleanup; + } + } + else + return GNUTLS_E_INTERNAL_ERROR; + + hd = _gnutls_hash_init (GNUTLS_MAC_SHA1); + if (hd == GNUTLS_HASH_FAILED) + { + gnutls_assert (); + result = GNUTLS_E_INTERNAL_ERROR; + goto cleanup; + } + + _gnutls_hash (hd, der.data, der.size); + + _gnutls_hash_deinit (hd, output_data); + *output_data_size = 20; + + result = 0; + +cleanup: + + _gnutls_free_datum (&der); + return result; +} + +#ifdef ENABLE_PKI + +/** + * gnutls_x509_privkey_sign_data - This function will sign the given data using the private key params + * @key: Holds the key + * @digest: should be MD5 or SHA1 + * @flags: should be 0 for now + * @data: holds the data to be signed + * @signature: will contain the signature + * @signature_size: holds the size of signature (and will be replaced + * by the new size) + * + * This function will sign the given data using a signature algorithm + * supported by the private key. Signature algorithms are always used + * together with a hash functions. Different hash functions may be + * used for the RSA algorithm, but only SHA-1 for the DSA keys. + * + * If the buffer provided is not long enough to hold the output, then + * *signature_size is updated and GNUTLS_E_SHORT_MEMORY_BUFFER will + * be returned. + * + * In case of failure a negative value will be returned, and + * 0 on success. + * + **/ +int +gnutls_x509_privkey_sign_data (gnutls_x509_privkey_t key, + gnutls_digest_algorithm_t digest, + unsigned int flags, + const gnutls_datum_t * data, + void *signature, size_t * signature_size) +{ + int result; + gnutls_datum_t sig = { NULL, 0 }; + + if (key == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + result = _gnutls_x509_sign (data, digest, key, &sig); + if (result < 0) + { + gnutls_assert (); + return result; + } + + if (*signature_size < sig.size) + { + *signature_size = sig.size; + _gnutls_free_datum (&sig); + return GNUTLS_E_SHORT_MEMORY_BUFFER; + } + + *signature_size = sig.size; + memcpy (signature, sig.data, sig.size); + + _gnutls_free_datum (&sig); + + return 0; +} + +/** + * gnutls_x509_privkey_sign_hash - This function will sign the given data using the private key params + * @key: Holds the key + * @hash: holds the data to be signed + * @signature: will contain newly allocated signature + * + * This function will sign the given hash using the private key. + * + * Return value: In case of failure a negative value will be returned, + * and 0 on success. + **/ +int +gnutls_x509_privkey_sign_hash (gnutls_x509_privkey_t key, + const gnutls_datum_t * hash, + gnutls_datum_t * signature) +{ + int result; + + if (key == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + result = _gnutls_sign (key->pk_algorithm, key->params, + key->params_size, hash, signature); + if (result < 0) + { + gnutls_assert (); + return result; + } + + return 0; +} + +/** + * gnutls_x509_privkey_verify_data - This function will verify the given signed data. + * @key: Holds the key + * @flags: should be 0 for now + * @data: holds the data to be signed + * @signature: contains the signature + * + * This function will verify the given signed data, using the parameters in the + * private key. + * + * In case of a verification failure 0 is returned, and + * 1 on success. + * + **/ +int +gnutls_x509_privkey_verify_data (gnutls_x509_privkey_t key, + unsigned int flags, + const gnutls_datum_t * data, + const gnutls_datum_t * signature) +{ + int result; + + if (key == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + result = _gnutls_x509_privkey_verify_signature (data, signature, key); + if (result < 0) + { + gnutls_assert (); + return 0; + } + + return result; +} + +/** + * gnutls_x509_privkey_fix - This function will recalculate some parameters of the key. + * @key: Holds the key + * + * This function will recalculate the secondary parameters in a key. + * In RSA keys, this can be the coefficient and exponent1,2. + * + * Return value: In case of failure a negative value will be + * returned, and 0 on success. + * + **/ +int +gnutls_x509_privkey_fix (gnutls_x509_privkey_t key) +{ + int ret; + + if (key == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + if (!key->crippled) + asn1_delete_structure (&key->key); + switch (key->pk_algorithm) + { + case GNUTLS_PK_RSA: + ret = _gnutls_asn1_encode_rsa (&key->key, key->params); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + break; + default: + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + return 0; +} + +#endif diff --git a/src/daemon/https/x509/x509_verify.c b/src/daemon/https/x509/x509_verify.c new file mode 100644 index 00000000..f01fed9d --- /dev/null +++ b/src/daemon/https/x509/x509_verify.c @@ -0,0 +1,1037 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2006, 2007 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +/* All functions which relate to X.509 certificate verification stuff are + * included here + */ + +#include +#include +#include +#include +#include +#include /* MAX */ +#include +#include +#include +#include +#include +#include +#include +#include + +static int _gnutls_verify_certificate2 (gnutls_x509_crt_t cert, + const gnutls_x509_crt_t * trusted_cas, + int tcas_size, + unsigned int flags, + unsigned int *output); +int _gnutls_x509_verify_signature (const gnutls_datum_t * signed_data, + const gnutls_datum_t * signature, + gnutls_x509_crt_t issuer); + +static + int is_crl_issuer (gnutls_x509_crl_t crl, gnutls_x509_crt_t issuer_cert); +static int _gnutls_verify_crl2 (gnutls_x509_crl_t crl, + const gnutls_x509_crt_t * trusted_cas, + int tcas_size, + unsigned int flags, unsigned int *output); + +/* Checks if the issuer of a certificate is a + * Certificate Authority, or if the certificate is the same + * as the issuer (and therefore it doesn't need to be a CA). + * + * Returns true or false, if the issuer is a CA, + * or not. + */ +static int +check_if_ca (gnutls_x509_crt_t cert, + gnutls_x509_crt_t issuer, unsigned int flags) +{ + gnutls_datum_t cert_signed_data = { NULL, + 0 + }; + gnutls_datum_t issuer_signed_data = { NULL, + 0 + }; + gnutls_datum_t cert_signature = { NULL, + 0 + }; + gnutls_datum_t issuer_signature = { NULL, + 0 + }; + int result; + + /* Check if the issuer is the same with the + * certificate. This is added in order for trusted + * certificates to be able to verify themselves. + */ + + result = _gnutls_x509_get_signed_data (issuer->cert, "tbsCertificate", + &issuer_signed_data); + if (result < 0) + { + gnutls_assert (); + goto cleanup; + } + + result = _gnutls_x509_get_signed_data (cert->cert, "tbsCertificate", + &cert_signed_data); + if (result < 0) + { + gnutls_assert (); + goto cleanup; + } + + result = _gnutls_x509_get_signature (issuer->cert, "signature", + &issuer_signature); + if (result < 0) + { + gnutls_assert (); + goto cleanup; + } + + result = + _gnutls_x509_get_signature (cert->cert, "signature", &cert_signature); + if (result < 0) + { + gnutls_assert (); + goto cleanup; + } + + /* If the subject certificate is the same as the issuer + * return true. + */ + if (!(flags & GNUTLS_VERIFY_DO_NOT_ALLOW_SAME)) + if (cert_signed_data.size == issuer_signed_data.size) + { + if ((memcmp (cert_signed_data.data, issuer_signed_data.data, + cert_signed_data.size) == 0) && (cert_signature.size + == + issuer_signature.size) + && + (memcmp + (cert_signature.data, issuer_signature.data, + cert_signature.size) == 0)) + { + result = 1; + goto cleanup; + } + } + + if (gnutls_x509_crt_get_ca_status (issuer, NULL) == 1) + { + result = 1; + goto cleanup; + } + else + gnutls_assert (); + + result = 0; + +cleanup:_gnutls_free_datum (&cert_signed_data); + _gnutls_free_datum (&issuer_signed_data); + _gnutls_free_datum (&cert_signature); + _gnutls_free_datum (&issuer_signature); + return result; +} + +/* This function checks if 'certs' issuer is 'issuer_cert'. + * This does a straight (DER) compare of the issuer/subject fields in + * the given certificates. + * + * Returns 1 if they match and zero if they don't match. Otherwise + * a negative value is returned to indicate error. + */ +static int +is_issuer (gnutls_x509_crt_t cert, gnutls_x509_crt_t issuer_cert) +{ + gnutls_datum_t dn1 = { NULL, + 0 + }, dn2 = + { + NULL, 0}; + int ret; + + ret = gnutls_x509_crt_get_raw_issuer_dn (cert, &dn1); + if (ret < 0) + { + gnutls_assert (); + goto cleanup; + } + + ret = gnutls_x509_crt_get_raw_dn (issuer_cert, &dn2); + if (ret < 0) + { + gnutls_assert (); + goto cleanup; + } + + ret = _gnutls_x509_compare_raw_dn (&dn1, &dn2); + +cleanup:_gnutls_free_datum (&dn1); + _gnutls_free_datum (&dn2); + return ret; + +} + +static inline gnutls_x509_crt_t +find_issuer (gnutls_x509_crt_t cert, + const gnutls_x509_crt_t * trusted_cas, int tcas_size) +{ + int i; + + /* this is serial search. + */ + + for (i = 0; i < tcas_size; i++) + { + if (is_issuer (cert, trusted_cas[i]) == 1) + return trusted_cas[i]; + } + + gnutls_assert (); + return NULL; +} + +/* + * Verifies the given certificate again a certificate list of + * trusted CAs. + * + * Returns only 0 or 1. If 1 it means that the certificate + * was successfuly verified. + * + * 'flags': an OR of the gnutls_certificate_verify_flags enumeration. + * + * Output will hold some extra information about the verification + * procedure. + */ +static int +_gnutls_verify_certificate2 (gnutls_x509_crt_t cert, + const gnutls_x509_crt_t * trusted_cas, + int tcas_size, + unsigned int flags, unsigned int *output) +{ + gnutls_datum_t cert_signed_data = { NULL, + 0 + }; + gnutls_datum_t cert_signature = { NULL, + 0 + }; + gnutls_x509_crt_t issuer; + int ret, issuer_version, result; + + if (output) + *output = 0; + + if (tcas_size >= 1) + issuer = find_issuer (cert, trusted_cas, tcas_size); + else + { + gnutls_assert (); + if (output) + *output |= GNUTLS_CERT_SIGNER_NOT_FOUND | GNUTLS_CERT_INVALID; + return 0; + } + + /* issuer is not in trusted certificate + * authorities. + */ + if (issuer == NULL) + { + if (output) + *output |= GNUTLS_CERT_SIGNER_NOT_FOUND | GNUTLS_CERT_INVALID; + gnutls_assert (); + return 0; + } + + issuer_version = gnutls_x509_crt_get_version (issuer); + if (issuer_version < 0) + { + gnutls_assert (); + return issuer_version; + } + + if (!(flags & GNUTLS_VERIFY_DISABLE_CA_SIGN) && !((flags + & + GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT) + && issuer_version == 1)) + { + if (check_if_ca (cert, issuer, flags) == 0) + { + gnutls_assert (); + if (output) + *output |= GNUTLS_CERT_SIGNER_NOT_CA | GNUTLS_CERT_INVALID; + return 0; + } + } + + result = _gnutls_x509_get_signed_data (cert->cert, "tbsCertificate", + &cert_signed_data); + if (result < 0) + { + gnutls_assert (); + goto cleanup; + } + + result = + _gnutls_x509_get_signature (cert->cert, "signature", &cert_signature); + if (result < 0) + { + gnutls_assert (); + goto cleanup; + } + + ret = _gnutls_x509_verify_signature (&cert_signed_data, &cert_signature, + issuer); + if (ret < 0) + { + gnutls_assert (); + } + else if (ret == 0) + { + gnutls_assert (); + /* error. ignore it */ + if (output) + *output |= GNUTLS_CERT_INVALID; + ret = 0; + } + + /* If the certificate is not self signed check if the algorithms + * used are secure. If the certificate is self signed it doesn't + * really matter. + */ + if (is_issuer (cert, cert) == 0) + { + int sigalg; + + sigalg = gnutls_x509_crt_get_signature_algorithm (cert); + + if (((sigalg == GNUTLS_SIGN_RSA_MD2) && !(flags + & + GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD2)) + || ((sigalg == GNUTLS_SIGN_RSA_MD5) + && !(flags & GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5))) + { + if (output) + *output |= GNUTLS_CERT_INSECURE_ALGORITHM | GNUTLS_CERT_INVALID; + } + } + + result = ret; + +cleanup:_gnutls_free_datum (&cert_signed_data); + _gnutls_free_datum (&cert_signature); + + return result; +} + +/** + * gnutls_x509_crt_check_issuer - This function checks if the certificate given has the given issuer + * @cert: is the certificate to be checked + * @issuer: is the certificate of a possible issuer + * + * This function will check if the given certificate was issued by the + * given issuer. It will return true (1) if the given certificate is issued + * by the given issuer, and false (0) if not. + * + * A negative value is returned in case of an error. + * + **/ +int +gnutls_x509_crt_check_issuer (gnutls_x509_crt_t cert, + gnutls_x509_crt_t issuer) +{ + return is_issuer (cert, issuer); +} + +/* The algorithm used is: + * 1. Check last certificate in the chain. If it is not verified return. + * 2. Check if any certificates in the chain are revoked. If yes return. + * 3. Try to verify the rest of certificates in the chain. If not verified return. + * 4. Return 0. + * + * Note that the return value is an OR of GNUTLS_CERT_* elements. + * + * This function verifies a X.509 certificate list. The certificate list should + * lead to a trusted CA in order to be trusted. + */ +static unsigned int +_gnutls_x509_verify_certificate (const gnutls_x509_crt_t * certificate_list, + int clist_size, + const gnutls_x509_crt_t * trusted_cas, + int tcas_size, + const gnutls_x509_crl_t * CRLs, + int crls_size, unsigned int flags) +{ + int i = 0, ret; + unsigned int status = 0, output; + + /* Verify the last certificate in the certificate path + * against the trusted CA certificate list. + * + * If no CAs are present returns CERT_INVALID. Thus works + * in self signed etc certificates. + */ + ret = _gnutls_verify_certificate2 (certificate_list[clist_size - 1], + trusted_cas, tcas_size, flags, &output); + + if (ret == 0) + { + /* if the last certificate in the certificate + * list is invalid, then the certificate is not + * trusted. + */ + gnutls_assert (); + status |= output; + status |= GNUTLS_CERT_INVALID; + return status; + } + + /* Check for revoked certificates in the chain + */ +#ifdef ENABLE_PKI + for (i = 0; i < clist_size; i++) + { + ret = gnutls_x509_crt_check_revocation (certificate_list[i], + CRLs, crls_size); + if (ret == 1) + { /* revoked */ + status |= GNUTLS_CERT_REVOKED; + status |= GNUTLS_CERT_INVALID; + return status; + } + } +#endif + + /* Check if the last certificate in the path is self signed. + * In that case ignore it (a certificate is trusted only if it + * leads to a trusted party by us, not the server's). + */ + if (gnutls_x509_crt_check_issuer (certificate_list[clist_size - 1], + certificate_list[clist_size - 1]) > 0 + && clist_size > 0) + { + clist_size--; + } + + /* Verify the certificate path (chain) + */ + for (i = clist_size - 1; i > 0; i--) + { + if (i - 1 < 0) + break; + + /* note that here we disable this V1 CA flag. So that no version 1 + * certificates can exist in a supplied chain. + */ + if (!(flags & GNUTLS_VERIFY_ALLOW_ANY_X509_V1_CA_CRT)) + flags ^= GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT; + if ((ret = _gnutls_verify_certificate2 (certificate_list[i - 1], + &certificate_list[i], 1, flags, + NULL)) == 0) + { + status |= GNUTLS_CERT_INVALID; + return status; + } + } + + return 0; +} + +/* Reads the digest information. + * we use DER here, although we should use BER. It works fine + * anyway. + */ +static int +decode_ber_digest_info (const gnutls_datum_t * info, + gnutls_mac_algorithm_t * hash, + opaque * digest, int *digest_size) +{ + ASN1_TYPE dinfo = ASN1_TYPE_EMPTY; + int result; + char str[1024]; + int len; + + if ((result = asn1_create_element (_gnutls_get_gnutls_asn (), + "GNUTLS.DigestInfo", + &dinfo)) != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + result = asn1_der_decoding (&dinfo, info->data, info->size, NULL); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + asn1_delete_structure (&dinfo); + return _gnutls_asn2err (result); + } + + len = sizeof (str) - 1; + result = asn1_read_value (dinfo, "digestAlgorithm.algorithm", str, &len); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + asn1_delete_structure (&dinfo); + return _gnutls_asn2err (result); + } + + *hash = _gnutls_x509_oid2mac_algorithm (str); + + if (*hash == GNUTLS_MAC_UNKNOWN) + { + + _gnutls_x509_log ("verify.c: HASH OID: %s\n", str); + + gnutls_assert (); + asn1_delete_structure (&dinfo); + return GNUTLS_E_UNKNOWN_ALGORITHM; + } + + len = sizeof (str) - 1; + result = asn1_read_value (dinfo, "digestAlgorithm.parameters", str, &len); + /* To avoid permitting garbage in the parameters field, either the + parameters field is not present, or it contains 0x05 0x00. */ + if (! + (result == ASN1_ELEMENT_NOT_FOUND + || (result == ASN1_SUCCESS && len == 2 && str[0] == 0x05 + && str[1] == 0x00))) + { + gnutls_assert (); + asn1_delete_structure (&dinfo); + return GNUTLS_E_ASN1_GENERIC_ERROR; + } + + result = asn1_read_value (dinfo, "digest", digest, digest_size); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + asn1_delete_structure (&dinfo); + return _gnutls_asn2err (result); + } + + asn1_delete_structure (&dinfo); + + return 0; +} + +/* if hash==MD5 then we do RSA-MD5 + * if hash==SHA then we do RSA-SHA + * params[0] is modulus + * params[1] is public key + */ +static int +_pkcs1_rsa_verify_sig (const gnutls_datum_t * text, + const gnutls_datum_t * signature, + mpi_t * params, int params_len) +{ + gnutls_mac_algorithm_t hash = GNUTLS_MAC_UNKNOWN; + int ret; + opaque digest[MAX_HASH_SIZE], md[MAX_HASH_SIZE]; + int digest_size; + GNUTLS_HASH_HANDLE hd; + gnutls_datum_t decrypted; + + ret = + _gnutls_pkcs1_rsa_decrypt (&decrypted, signature, params, params_len, 1); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + /* decrypted is a BER encoded data of type DigestInfo + */ + + digest_size = sizeof (digest); + if ((ret = decode_ber_digest_info (&decrypted, &hash, digest, &digest_size)) + != 0) + { + gnutls_assert (); + _gnutls_free_datum (&decrypted); + return ret; + } + + _gnutls_free_datum (&decrypted); + + if (digest_size != _gnutls_hash_get_algo_len (hash)) + { + gnutls_assert (); + return GNUTLS_E_ASN1_GENERIC_ERROR; + } + + hd = _gnutls_hash_init (hash); + if (hd == NULL) + { + gnutls_assert (); + return GNUTLS_E_HASH_FAILED; + } + + _gnutls_hash (hd, text->data, text->size); + _gnutls_hash_deinit (hd, md); + + if (memcmp (md, digest, digest_size) != 0) + { + gnutls_assert (); + return GNUTLS_E_PK_SIG_VERIFY_FAILED; + } + + return 0; +} + +/* Hashes input data and verifies a DSA signature. + */ +static int +dsa_verify_sig (const gnutls_datum_t * text, + const gnutls_datum_t * signature, + mpi_t * params, int params_len) +{ + int ret; + opaque _digest[MAX_HASH_SIZE]; + gnutls_datum_t digest; + GNUTLS_HASH_HANDLE hd; + + hd = _gnutls_hash_init (GNUTLS_MAC_SHA1); + if (hd == NULL) + { + gnutls_assert (); + return GNUTLS_E_HASH_FAILED; + } + + _gnutls_hash (hd, text->data, text->size); + _gnutls_hash_deinit (hd, _digest); + + digest.data = _digest; + digest.size = 20; + + ret = _gnutls_dsa_verify (&digest, signature, params, params_len); + + return ret; +} + +/* Verifies the signature data, and returns 0 if not verified, + * or 1 otherwise. + */ +static int +verify_sig (const gnutls_datum_t * tbs, + const gnutls_datum_t * signature, + gnutls_pk_algorithm_t pk, + mpi_t * issuer_params, int issuer_params_size) +{ + + switch (pk) + { + case GNUTLS_PK_RSA: + + if (_pkcs1_rsa_verify_sig + (tbs, signature, issuer_params, issuer_params_size) != 0) + { + gnutls_assert (); + return 0; + } + + return 1; + break; + + default: + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + + } +} + +/* verifies if the certificate is properly signed. + * returns 0 on failure and 1 on success. + * + * 'tbs' is the signed data + * 'signature' is the signature! + */ +int +_gnutls_x509_verify_signature (const gnutls_datum_t * tbs, + const gnutls_datum_t * signature, + gnutls_x509_crt_t issuer) +{ + mpi_t issuer_params[MAX_PUBLIC_PARAMS_SIZE]; + int ret, issuer_params_size, i; + + /* Read the MPI parameters from the issuer's certificate. + */ + issuer_params_size = MAX_PUBLIC_PARAMS_SIZE; + ret = + _gnutls_x509_crt_get_mpis (issuer, issuer_params, &issuer_params_size); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + ret = verify_sig (tbs, signature, gnutls_x509_crt_get_pk_algorithm (issuer, + NULL), + issuer_params, issuer_params_size); + if (ret < 0) + { + gnutls_assert (); + } + + /* release all allocated MPIs + */ + for (i = 0; i < issuer_params_size; i++) + { + _gnutls_mpi_release (&issuer_params[i]); + } + + return ret; +} + +/* verifies if the certificate is properly signed. + * returns 0 on failure and 1 on success. + * + * 'tbs' is the signed data + * 'signature' is the signature! + */ +int +_gnutls_x509_privkey_verify_signature (const gnutls_datum_t * tbs, + const gnutls_datum_t * signature, + gnutls_x509_privkey_t issuer) +{ + int ret; + + ret = verify_sig (tbs, signature, issuer->pk_algorithm, issuer->params, + issuer->params_size); + if (ret < 0) + { + gnutls_assert (); + } + + return ret; +} + +/** + * gnutls_x509_crt_list_verify - This function verifies the given certificate list + * @cert_list: is the certificate list to be verified + * @cert_list_length: holds the number of certificate in cert_list + * @CA_list: is the CA list which will be used in verification + * @CA_list_length: holds the number of CA certificate in CA_list + * @CRL_list: holds a list of CRLs. + * @CRL_list_length: the length of CRL list. + * @flags: Flags that may be used to change the verification algorithm. Use OR of the gnutls_certificate_verify_flags enumerations. + * @verify: will hold the certificate verification output. + * + * This function will try to verify the given certificate list and return its status. + * Note that expiration and activation dates are not checked + * by this function, you should check them using the appropriate functions. + * + * If no flags are specified (0), this function will use the + * basicConstraints (2.5.29.19) PKIX extension. This means that only a certificate + * authority is allowed to sign a certificate. + * + * You must also check the peer's name in order to check if the verified + * certificate belongs to the actual peer. + * + * The certificate verification output will be put in @verify and will be + * one or more of the gnutls_certificate_status_t enumerated elements bitwise or'd. + * For a more detailed verification status use gnutls_x509_crt_verify() per list + * element. + * + * GNUTLS_CERT_INVALID: the certificate chain is not valid. + * + * GNUTLS_CERT_REVOKED: a certificate in the chain has been revoked. + * + * Returns 0 on success and a negative value in case of an error. + * + **/ +int +gnutls_x509_crt_list_verify (const gnutls_x509_crt_t * cert_list, + int cert_list_length, + const gnutls_x509_crt_t * CA_list, + int CA_list_length, + const gnutls_x509_crl_t * CRL_list, + int CRL_list_length, + unsigned int flags, unsigned int *verify) +{ + if (cert_list == NULL || cert_list_length == 0) + return GNUTLS_E_NO_CERTIFICATE_FOUND; + + /* Verify certificate + */ + *verify = _gnutls_x509_verify_certificate (cert_list, cert_list_length, + CA_list, CA_list_length, + CRL_list, CRL_list_length, + flags); + + return 0; +} + +/** + * gnutls_x509_crt_verify - This function verifies the given certificate against a given trusted one + * @cert: is the certificate to be verified + * @CA_list: is one certificate that is considered to be trusted one + * @CA_list_length: holds the number of CA certificate in CA_list + * @flags: Flags that may be used to change the verification algorithm. Use OR of the gnutls_certificate_verify_flags enumerations. + * @verify: will hold the certificate verification output. + * + * This function will try to verify the given certificate and return its status. + * The verification output in this functions cannot be GNUTLS_CERT_NOT_VALID. + * + * Returns 0 on success and a negative value in case of an error. + * + **/ +int +gnutls_x509_crt_verify (gnutls_x509_crt_t cert, + const gnutls_x509_crt_t * CA_list, + int CA_list_length, + unsigned int flags, unsigned int *verify) +{ + int ret; + /* Verify certificate + */ + ret = _gnutls_verify_certificate2 (cert, CA_list, CA_list_length, flags, + verify); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + return 0; +} + +#ifdef ENABLE_PKI + +/** + * gnutls_x509_crl_check_issuer - This function checks if the CRL given has the given issuer + * @crl: is the CRL to be checked + * @issuer: is the certificate of a possible issuer + * + * This function will check if the given CRL was issued by the + * given issuer certificate. It will return true (1) if the given CRL was issued + * by the given issuer, and false (0) if not. + * + * A negative value is returned in case of an error. + * + **/ +int +gnutls_x509_crl_check_issuer (gnutls_x509_crl_t cert, + gnutls_x509_crt_t issuer) +{ + return is_crl_issuer (cert, issuer); +} + +/** + * gnutls_x509_crl_verify - This function verifies the given crl against a given trusted one + * @crl: is the crl to be verified + * @CA_list: is a certificate list that is considered to be trusted one + * @CA_list_length: holds the number of CA certificates in CA_list + * @flags: Flags that may be used to change the verification algorithm. Use OR of the gnutls_certificate_verify_flags enumerations. + * @verify: will hold the crl verification output. + * + * This function will try to verify the given crl and return its status. + * See gnutls_x509_crt_list_verify() for a detailed description of + * return values. + * + * Returns 0 on success and a negative value in case of an error. + * + **/ +int +gnutls_x509_crl_verify (gnutls_x509_crl_t crl, + const gnutls_x509_crt_t * CA_list, + int CA_list_length, unsigned int flags, + unsigned int *verify) +{ + int ret; + /* Verify crl + */ + ret = _gnutls_verify_crl2 (crl, CA_list, CA_list_length, flags, verify); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + return 0; +} + +/* The same as above, but here we've got a CRL. + */ +static int +is_crl_issuer (gnutls_x509_crl_t crl, gnutls_x509_crt_t issuer_cert) +{ + gnutls_datum_t dn1 = { NULL, 0 }, dn2 = + { + NULL, 0}; + int ret; + + ret = _gnutls_x509_crl_get_raw_issuer_dn (crl, &dn1); + if (ret < 0) + { + gnutls_assert (); + goto cleanup; + } + + ret = gnutls_x509_crt_get_raw_dn (issuer_cert, &dn2); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + ret = _gnutls_x509_compare_raw_dn (&dn1, &dn2); + +cleanup: + _gnutls_free_datum (&dn1); + _gnutls_free_datum (&dn2); + + return ret; +} + +static inline gnutls_x509_crt_t +find_crl_issuer (gnutls_x509_crl_t crl, + const gnutls_x509_crt_t * trusted_cas, int tcas_size) +{ + int i; + + /* this is serial search. + */ + + for (i = 0; i < tcas_size; i++) + { + if (is_crl_issuer (crl, trusted_cas[i]) == 1) + return trusted_cas[i]; + } + + gnutls_assert (); + return NULL; +} + +/* + * Returns only 0 or 1. If 1 it means that the CRL + * was successfuly verified. + * + * 'flags': an OR of the gnutls_certificate_verify_flags enumeration. + * + * Output will hold information about the verification + * procedure. + */ +static int +_gnutls_verify_crl2 (gnutls_x509_crl_t crl, + const gnutls_x509_crt_t * trusted_cas, + int tcas_size, unsigned int flags, unsigned int *output) +{ + /* CRL is ignored for now */ + gnutls_datum_t crl_signed_data = { NULL, 0 }; + gnutls_datum_t crl_signature = { NULL, 0 }; + gnutls_x509_crt_t issuer; + int ret, result; + + if (output) + *output = 0; + + if (tcas_size >= 1) + issuer = find_crl_issuer (crl, trusted_cas, tcas_size); + else + { + gnutls_assert (); + if (output) + *output |= GNUTLS_CERT_SIGNER_NOT_FOUND | GNUTLS_CERT_INVALID; + return 0; + } + + /* issuer is not in trusted certificate + * authorities. + */ + if (issuer == NULL) + { + gnutls_assert (); + if (output) + *output |= GNUTLS_CERT_SIGNER_NOT_FOUND | GNUTLS_CERT_INVALID; + return 0; + } + + if (!(flags & GNUTLS_VERIFY_DISABLE_CA_SIGN)) + { + if (gnutls_x509_crt_get_ca_status (issuer, NULL) != 1) + { + gnutls_assert (); + if (output) + *output |= GNUTLS_CERT_SIGNER_NOT_CA | GNUTLS_CERT_INVALID; + return 0; + } + } + + result = + _gnutls_x509_get_signed_data (crl->crl, "tbsCertList", &crl_signed_data); + if (result < 0) + { + gnutls_assert (); + goto cleanup; + } + + result = _gnutls_x509_get_signature (crl->crl, "signature", &crl_signature); + if (result < 0) + { + gnutls_assert (); + goto cleanup; + } + + ret = + _gnutls_x509_verify_signature (&crl_signed_data, &crl_signature, issuer); + if (ret < 0) + { + gnutls_assert (); + } + else if (ret == 0) + { + gnutls_assert (); + /* error. ignore it */ + if (output) + *output |= GNUTLS_CERT_INVALID; + ret = 0; + } + + { + int sigalg; + + sigalg = gnutls_x509_crl_get_signature_algorithm (crl); + + if (((sigalg == GNUTLS_SIGN_RSA_MD2) && + !(flags & GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD2)) || + ((sigalg == GNUTLS_SIGN_RSA_MD5) && + !(flags & GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5))) + { + if (output) + *output |= GNUTLS_CERT_INSECURE_ALGORITHM | GNUTLS_CERT_INVALID; + } + } + + result = ret; + +cleanup: + _gnutls_free_datum (&crl_signed_data); + _gnutls_free_datum (&crl_signature); + + return result; +} + +#endif diff --git a/src/daemon/https/x509/x509_write.c b/src/daemon/https/x509/x509_write.c new file mode 100644 index 00000000..d9529c33 --- /dev/null +++ b/src/daemon/https/x509/x509_write.c @@ -0,0 +1,1094 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2006, 2007 Free Software Foundation + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + */ + +/* This file contains functions to handle X.509 certificate generation. + */ + +#include + +#ifdef ENABLE_PKI + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void disable_optional_stuff (gnutls_x509_crt_t cert); + +/** + * gnutls_x509_crt_set_dn_by_oid - This function will set the Certificate request subject's distinguished name + * @crt: should contain a gnutls_x509_crt_t structure + * @oid: holds an Object Identifier in a null terminated string + * @raw_flag: must be 0, or 1 if the data are DER encoded + * @name: a pointer to the name + * @sizeof_name: holds the size of @name + * + * This function will set the part of the name of the Certificate subject, specified + * by the given OID. The input string should be ASCII or UTF-8 encoded. + * + * Some helper macros with popular OIDs can be found in gnutls/x509.h + * With this function you can only set the known OIDs. You can test + * for known OIDs using gnutls_x509_dn_oid_known(). For OIDs that are + * not known (by gnutls) you should properly DER encode your data, and + * call this function with raw_flag set. + * + * Returns 0 on success. + * + **/ +int +gnutls_x509_crt_set_dn_by_oid (gnutls_x509_crt_t crt, const char *oid, + unsigned int raw_flag, const void *name, + unsigned int sizeof_name) +{ + if (sizeof_name == 0 || name == NULL || crt == NULL) + { + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_set_dn_oid (crt->cert, "tbsCertificate.subject", + oid, raw_flag, name, sizeof_name); +} + +/** + * gnutls_x509_crt_set_issuer_dn_by_oid - This function will set the Certificate request issuer's distinguished name + * @crt: should contain a gnutls_x509_crt_t structure + * @oid: holds an Object Identifier in a null terminated string + * @raw_flag: must be 0, or 1 if the data are DER encoded + * @name: a pointer to the name + * @sizeof_name: holds the size of @name + * + * This function will set the part of the name of the Certificate issuer, specified + * by the given OID. The input string should be ASCII or UTF-8 encoded. + * + * Some helper macros with popular OIDs can be found in gnutls/x509.h + * With this function you can only set the known OIDs. You can test + * for known OIDs using gnutls_x509_dn_oid_known(). For OIDs that are + * not known (by gnutls) you should properly DER encode your data, and + * call this function with raw_flag set. + * + * Normally you do not need to call this function, since the signing + * operation will copy the signer's name as the issuer of the certificate. + * + * Returns 0 on success. + * + **/ +int +gnutls_x509_crt_set_issuer_dn_by_oid (gnutls_x509_crt_t crt, + const char *oid, + unsigned int raw_flag, + const void *name, + unsigned int sizeof_name) +{ + if (sizeof_name == 0 || name == NULL || crt == NULL) + { + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_set_dn_oid (crt->cert, "tbsCertificate.issuer", oid, + raw_flag, name, sizeof_name); +} + +/** + * gnutls_x509_crt_set_proxy_dn - Set Proxy Certificate subject's distinguished name + * @crt: a gnutls_x509_crt_t structure with the new proxy cert + * @eecrt: the end entity certificate that will be issuing the proxy + * @raw_flag: must be 0, or 1 if the CN is DER encoded + * @name: a pointer to the CN name, may be NULL (but MUST then be added later) + * @sizeof_name: holds the size of @name + * + * This function will set the subject in @crt to the end entity's + * @eecrt subject name, and add a single Common Name component @name + * of size @sizeof_name. This corresponds to the required proxy + * certificate naming style. Note that if @name is %NULL, you MUST + * set it later by using gnutls_x509_crt_set_dn_by_oid() or similar. + * + * Returns 0 on success. + * + **/ +int +gnutls_x509_crt_set_proxy_dn (gnutls_x509_crt_t crt, gnutls_x509_crt_t eecrt, + unsigned int raw_flag, const void *name, + unsigned int sizeof_name) +{ + int result; + + if (crt == NULL || eecrt == NULL) + { + return GNUTLS_E_INVALID_REQUEST; + } + + result = asn1_copy_node (crt->cert, "tbsCertificate.subject", + eecrt->cert, "tbsCertificate.subject"); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + if (name && sizeof_name) + { + return _gnutls_x509_set_dn_oid (crt->cert, "tbsCertificate.subject", + GNUTLS_OID_X520_COMMON_NAME, + raw_flag, name, sizeof_name); + } + + return 0; +} + +/** + * gnutls_x509_crt_set_version - This function will set the Certificate request version + * @crt: should contain a gnutls_x509_crt_t structure + * @version: holds the version number. For X.509v1 certificates must be 1. + * + * This function will set the version of the certificate. This must + * be one for X.509 version 1, and so on. Plain certificates without + * extensions must have version set to one. + * + * To create well-formed certificates, you must specify version 3 if + * you use any certificate extensions. Extensions are created by + * functions such as gnutls_x509_crt_set_subject_alternative_name or + * gnutls_x509_crt_set_key_usage. + * + * Returns 0 on success. + * + **/ +int +gnutls_x509_crt_set_version (gnutls_x509_crt_t crt, unsigned int version) +{ + int result; + unsigned char null = version; + + if (crt == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + if (null > 0) + null--; + + result = asn1_write_value (crt->cert, "tbsCertificate.version", &null, 1); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + return 0; +} + +/** + * gnutls_x509_crt_set_key - This function will associate the Certificate with a key + * @crt: should contain a gnutls_x509_crt_t structure + * @key: holds a private key + * + * This function will set the public parameters from the given private key to the + * certificate. Only RSA keys are currently supported. + * + * Returns 0 on success. + * + **/ +int +gnutls_x509_crt_set_key (gnutls_x509_crt_t crt, gnutls_x509_privkey_t key) +{ + int result; + + if (crt == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + result = _gnutls_x509_encode_and_copy_PKI_params (crt->cert, + "tbsCertificate.subjectPublicKeyInfo", + key->pk_algorithm, + key->params, + key->params_size); + + if (result < 0) + { + gnutls_assert (); + return result; + } + + return 0; +} + +/** + * gnutls_x509_crt_set_crq - This function will associate the Certificate with a request + * @crt: should contain a gnutls_x509_crt_t structure + * @crq: holds a certificate request + * + * This function will set the name and public parameters from the given certificate request to the + * certificate. Only RSA keys are currently supported. + * + * Returns 0 on success. + * + **/ +int +gnutls_x509_crt_set_crq (gnutls_x509_crt_t crt, gnutls_x509_crq_t crq) +{ + int result; + int pk_algorithm; + + if (crt == NULL || crq == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + pk_algorithm = gnutls_x509_crq_get_pk_algorithm (crq, NULL); + + result = asn1_copy_node (crt->cert, "tbsCertificate.subject", + crq->crq, "certificationRequestInfo.subject"); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + result = + asn1_copy_node (crt->cert, "tbsCertificate.subjectPublicKeyInfo", + crq->crq, "certificationRequestInfo.subjectPKInfo"); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + return 0; +} + +/** + * gnutls_x509_crt_set_extension_by_oid - This function will set an arbitrary extension + * @crt: should contain a gnutls_x509_crt_t structure + * @oid: holds an Object Identified in null terminated string + * @buf: a pointer to a DER encoded data + * @sizeof_buf: holds the size of @buf + * @critical: should be non zero if the extension is to be marked as critical + * + * This function will set an the extension, by the specified OID, in the certificate. + * The extension data should be binary data DER encoded. + * + * Returns 0 on success and a negative value in case of an error. + * + **/ +int +gnutls_x509_crt_set_extension_by_oid (gnutls_x509_crt_t crt, + const char *oid, const void *buf, + size_t sizeof_buf, + unsigned int critical) +{ + int result; + gnutls_datum_t der_data; + + der_data.data = (void *) buf; + der_data.size = sizeof_buf; + + if (crt == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + result = _gnutls_x509_crt_set_extension (crt, oid, &der_data, critical); + if (result < 0) + { + gnutls_assert (); + return result; + } + + crt->use_extensions = 1; + + return 0; + +} + +/** + * gnutls_x509_crt_set_basic_constraints - This function will set the basicConstraints extension + * @crt: should contain a gnutls_x509_crt_t structure + * @ca: true(1) or false(0). Depending on the Certificate authority status. + * @pathLenConstraint: non-negative values indicate maximum length of path, + * and negative values indicate that the pathLenConstraints field should + * not be present. + * + * This function will set the basicConstraints certificate extension. + * + * Returns 0 on success. + * + **/ +int +gnutls_x509_crt_set_basic_constraints (gnutls_x509_crt_t crt, + unsigned int ca, int pathLenConstraint) +{ + int result; + gnutls_datum_t der_data; + + if (crt == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + /* generate the extension. + */ + result = _gnutls_x509_ext_gen_basicConstraints (ca, pathLenConstraint, + &der_data); + if (result < 0) + { + gnutls_assert (); + return result; + } + + result = _gnutls_x509_crt_set_extension (crt, "2.5.29.19", &der_data, 1); + + _gnutls_free_datum (&der_data); + + if (result < 0) + { + gnutls_assert (); + return result; + } + + crt->use_extensions = 1; + + return 0; +} + +/** + * gnutls_x509_crt_set_ca_status - This function will set the basicConstraints extension + * @crt: should contain a gnutls_x509_crt_t structure + * @ca: true(1) or false(0). Depending on the Certificate authority status. + * + * This function will set the basicConstraints certificate extension. + * Use gnutls_x509_crt_set_basic_constraints() if you want to control + * the pathLenConstraint field too. + * + * Returns 0 on success. + * + **/ +int +gnutls_x509_crt_set_ca_status (gnutls_x509_crt_t crt, unsigned int ca) +{ + return gnutls_x509_crt_set_basic_constraints (crt, ca, -1); +} + +/** + * gnutls_x509_crt_set_key_usage - This function will set the keyUsage extension + * @crt: should contain a gnutls_x509_crt_t structure + * @usage: an ORed sequence of the GNUTLS_KEY_* elements. + * + * This function will set the keyUsage certificate extension. + * + * Returns 0 on success. + * + **/ +int +gnutls_x509_crt_set_key_usage (gnutls_x509_crt_t crt, unsigned int usage) +{ + int result; + gnutls_datum_t der_data; + + if (crt == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + /* generate the extension. + */ + result = _gnutls_x509_ext_gen_keyUsage ((uint16_t) usage, &der_data); + if (result < 0) + { + gnutls_assert (); + return result; + } + + result = _gnutls_x509_crt_set_extension (crt, "2.5.29.15", &der_data, 1); + + _gnutls_free_datum (&der_data); + + if (result < 0) + { + gnutls_assert (); + return result; + } + + crt->use_extensions = 1; + + return 0; +} + +/** + * gnutls_x509_crt_set_subject_alternative_name - This function will set the subject Alternative Name + * @crt: should contain a gnutls_x509_crt_t structure + * @type: is one of the gnutls_x509_subject_alt_name_t enumerations + * @data_string: The data to be set + * + * This function will set the subject alternative name certificate extension. + * + * Returns 0 on success. + * + **/ +int +gnutls_x509_crt_set_subject_alternative_name (gnutls_x509_crt_t crt, + gnutls_x509_subject_alt_name_t + type, const char *data_string) +{ + int result; + gnutls_datum_t der_data; + gnutls_datum_t dnsname; + unsigned int critical; + + if (crt == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + /* Check if the extension already exists. + */ + result = + _gnutls_x509_crt_get_extension (crt, "2.5.29.17", 0, &dnsname, &critical); + + if (result >= 0) + _gnutls_free_datum (&dnsname); + if (result != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + /* generate the extension. + */ + result = + _gnutls_x509_ext_gen_subject_alt_name (type, data_string, &der_data); + if (result < 0) + { + gnutls_assert (); + return result; + } + + result = _gnutls_x509_crt_set_extension (crt, "2.5.29.17", &der_data, 0); + + _gnutls_free_datum (&der_data); + + if (result < 0) + { + gnutls_assert (); + return result; + } + + crt->use_extensions = 1; + + return 0; +} + +/** + * gnutls_x509_crt_set_proxy - Set the proxyCertInfo extension + * @crt: should contain a gnutls_x509_crt_t structure + * @pathLenConstraint: non-negative values indicate maximum length of path, + * and negative values indicate that the pathLenConstraints field should + * not be present. + * @policyLanguage: OID describing the language of @policy. + * @policy: opaque byte array with policy language, can be %NULL + * @sizeof_policy: size of @policy. + * + * This function will set the proxyCertInfo extension. + * + * Returns 0 on success. + * + **/ +int +gnutls_x509_crt_set_proxy (gnutls_x509_crt_t crt, + int pathLenConstraint, + const char *policyLanguage, + const char *policy, size_t sizeof_policy) +{ + int result; + gnutls_datum_t der_data; + + if (crt == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + /* generate the extension. + */ + result = _gnutls_x509_ext_gen_proxyCertInfo (pathLenConstraint, + policyLanguage, + policy, sizeof_policy, + &der_data); + if (result < 0) + { + gnutls_assert (); + return result; + } + + result = _gnutls_x509_crt_set_extension (crt, "1.3.6.1.5.5.7.1.14", + &der_data, 1); + + _gnutls_free_datum (&der_data); + + if (result < 0) + { + gnutls_assert (); + return result; + } + + crt->use_extensions = 1; + + return 0; +} + +/** + * gnutls_x509_crt_sign2 - This function will sign a certificate with a key + * @crt: should contain a gnutls_x509_crt_t structure + * @issuer: is the certificate of the certificate issuer + * @issuer_key: holds the issuer's private key + * @dig: The message digest to use. GNUTLS_DIG_SHA1 is the safe choice unless you know what you're doing. + * @flags: must be 0 + * + * This function will sign the certificate with the issuer's private key, and + * will copy the issuer's information into the certificate. + * + * This must be the last step in a certificate generation since all + * the previously set parameters are now signed. + * + * Returns 0 on success. + * + **/ +int +gnutls_x509_crt_sign2 (gnutls_x509_crt_t crt, gnutls_x509_crt_t issuer, + gnutls_x509_privkey_t issuer_key, + gnutls_digest_algorithm_t dig, unsigned int flags) +{ + int result; + + if (crt == NULL || issuer == NULL || issuer_key == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + /* disable all the unneeded OPTIONAL fields. + */ + disable_optional_stuff (crt); + + result = _gnutls_x509_pkix_sign (crt->cert, "tbsCertificate", + dig, issuer, issuer_key); + if (result < 0) + { + gnutls_assert (); + return result; + } + + return 0; +} + +/** + * gnutls_x509_crt_sign - This function will sign a certificate with a key + * @crt: should contain a gnutls_x509_crt_t structure + * @issuer: is the certificate of the certificate issuer + * @issuer_key: holds the issuer's private key + * + * This function is the same a gnutls_x509_crt_sign2() with no flags, and + * SHA1 as the hash algorithm. + * + * Returns 0 on success. + * + **/ +int +gnutls_x509_crt_sign (gnutls_x509_crt_t crt, gnutls_x509_crt_t issuer, + gnutls_x509_privkey_t issuer_key) +{ + return gnutls_x509_crt_sign2 (crt, issuer, issuer_key, GNUTLS_DIG_SHA1, 0); +} + +/** + * gnutls_x509_crt_set_activation_time - This function will set the Certificate's activation time + * @cert: should contain a gnutls_x509_crt_t structure + * @act_time: The actual time + * + * This function will set the time this Certificate was or will be activated. + * + * Returns 0 on success, or a negative value in case of an error. + * + **/ +int +gnutls_x509_crt_set_activation_time (gnutls_x509_crt_t cert, time_t act_time) +{ + if (cert == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + return _gnutls_x509_set_time (cert->cert, + "tbsCertificate.validity.notBefore", + act_time); +} + +/** + * gnutls_x509_crt_set_expiration_time - This function will set the Certificate's expiration time + * @cert: should contain a gnutls_x509_crt_t structure + * @exp_time: The actual time + * + * This function will set the time this Certificate will expire. + * + * Returns 0 on success, or a negative value in case of an error. + * + **/ +int +gnutls_x509_crt_set_expiration_time (gnutls_x509_crt_t cert, time_t exp_time) +{ + if (cert == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + return _gnutls_x509_set_time (cert->cert, + "tbsCertificate.validity.notAfter", exp_time); +} + +/** + * gnutls_x509_crt_set_serial - This function will set the certificate's serial number + * @cert: should contain a gnutls_x509_crt_t structure + * @serial: The serial number + * @serial_size: Holds the size of the serial field. + * + * This function will set the X.509 certificate's serial number. + * Serial is not always a 32 or 64bit number. Some CAs use + * large serial numbers, thus it may be wise to handle it as something + * opaque. + * + * Returns 0 on success, or a negative value in case of an error. + * + **/ +int +gnutls_x509_crt_set_serial (gnutls_x509_crt_t cert, const void *serial, + size_t serial_size) +{ + int ret; + + if (cert == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = + asn1_write_value (cert->cert, "tbsCertificate.serialNumber", serial, + serial_size); + if (ret != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (ret); + } + + return 0; + +} + +/* If OPTIONAL fields have not been initialized then + * disable them. + */ +static void +disable_optional_stuff (gnutls_x509_crt_t cert) +{ + + asn1_write_value (cert->cert, "tbsCertificate.issuerUniqueID", NULL, 0); + + asn1_write_value (cert->cert, "tbsCertificate.subjectUniqueID", NULL, 0); + + if (cert->use_extensions == 0) + { + _gnutls_x509_log ("Disabling X.509 extensions.\n"); + asn1_write_value (cert->cert, "tbsCertificate.extensions", NULL, 0); + } + + return; +} + +/** + * gnutls_x509_crt_set_crl_dist_points - This function will set the CRL dist points + * @crt: should contain a gnutls_x509_crt_t structure + * @type: is one of the gnutls_x509_subject_alt_name_t enumerations + * @data_string: The data to be set + * @reason_flags: revocation reasons + * + * This function will set the CRL distribution points certificate extension. + * + * Returns 0 on success. + * + **/ +int +gnutls_x509_crt_set_crl_dist_points (gnutls_x509_crt_t crt, + gnutls_x509_subject_alt_name_t + type, const void *data_string, + unsigned int reason_flags) +{ + int result; + gnutls_datum_t der_data; + gnutls_datum_t oldname; + unsigned int critical; + + if (crt == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + /* Check if the extension already exists. + */ + result = + _gnutls_x509_crt_get_extension (crt, "2.5.29.31", 0, &oldname, &critical); + + if (result >= 0) + _gnutls_free_datum (&oldname); + if (result != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + /* generate the extension. + */ + result = + _gnutls_x509_ext_gen_crl_dist_points (type, data_string, + reason_flags, &der_data); + if (result < 0) + { + gnutls_assert (); + return result; + } + + result = _gnutls_x509_crt_set_extension (crt, "2.5.29.31", &der_data, 0); + + _gnutls_free_datum (&der_data); + + if (result < 0) + { + gnutls_assert (); + return result; + } + + crt->use_extensions = 1; + + return 0; +} + +/** + * gnutls_x509_crt_cpy_crl_dist_points - This function will copy the CRL dist points + * @dst: should contain a gnutls_x509_crt_t structure + * @src: the certificate where the dist points will be copied from + * + * This function will copy the CRL distribution points certificate + * extension, from the source to the destination certificate. + * This may be useful to copy from a CA certificate to issued ones. + * + * Returns 0 on success. + * + **/ +int +gnutls_x509_crt_cpy_crl_dist_points (gnutls_x509_crt_t dst, + gnutls_x509_crt_t src) +{ + int result; + gnutls_datum_t der_data; + unsigned int critical; + + if (dst == NULL || src == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + /* Check if the extension already exists. + */ + result = + _gnutls_x509_crt_get_extension (src, "2.5.29.31", 0, &der_data, + &critical); + if (result < 0) + { + gnutls_assert (); + return result; + } + + result = + _gnutls_x509_crt_set_extension (dst, "2.5.29.31", &der_data, critical); + _gnutls_free_datum (&der_data); + + if (result < 0) + { + gnutls_assert (); + return result; + } + + dst->use_extensions = 1; + + return 0; +} + +/** + * gnutls_x509_crt_set_subject_key_id - This function will set the certificate's subject key id + * @cert: should contain a gnutls_x509_crt_t structure + * @id: The key ID + * @id_size: Holds the size of the serial field. + * + * This function will set the X.509 certificate's subject key ID extension. + * + * Returns 0 on success, or a negative value in case of an error. + * + **/ +int +gnutls_x509_crt_set_subject_key_id (gnutls_x509_crt_t cert, + const void *id, size_t id_size) +{ + int result; + gnutls_datum_t old_id, der_data; + unsigned int critical; + + if (cert == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + /* Check if the extension already exists. + */ + result = + _gnutls_x509_crt_get_extension (cert, "2.5.29.14", 0, &old_id, &critical); + + if (result >= 0) + _gnutls_free_datum (&old_id); + if (result != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + /* generate the extension. + */ + result = _gnutls_x509_ext_gen_key_id (id, id_size, &der_data); + if (result < 0) + { + gnutls_assert (); + return result; + } + + result = _gnutls_x509_crt_set_extension (cert, "2.5.29.14", &der_data, 0); + + _gnutls_free_datum (&der_data); + + if (result < 0) + { + gnutls_assert (); + return result; + } + + cert->use_extensions = 1; + + return 0; +} + +/** + * gnutls_x509_crt_set_authority_key_id - This function will set the certificate authority's key id + * @cert: should contain a gnutls_x509_crt_t structure + * @id: The key ID + * @id_size: Holds the size of the serial field. + * + * This function will set the X.509 certificate's authority key ID extension. + * Only the keyIdentifier field can be set with this function. + * + * Returns 0 on success, or a negative value in case of an error. + * + **/ +int +gnutls_x509_crt_set_authority_key_id (gnutls_x509_crt_t cert, + const void *id, size_t id_size) +{ + int result; + gnutls_datum_t old_id, der_data; + unsigned int critical; + + if (cert == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + /* Check if the extension already exists. + */ + result = + _gnutls_x509_crt_get_extension (cert, "2.5.29.35", 0, &old_id, &critical); + + if (result >= 0) + _gnutls_free_datum (&old_id); + if (result != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + /* generate the extension. + */ + result = _gnutls_x509_ext_gen_auth_key_id (id, id_size, &der_data); + if (result < 0) + { + gnutls_assert (); + return result; + } + + result = _gnutls_x509_crt_set_extension (cert, "2.5.29.35", &der_data, 0); + + _gnutls_free_datum (&der_data); + + if (result < 0) + { + gnutls_assert (); + return result; + } + + cert->use_extensions = 1; + + return 0; +} + +/** + * gnutls_x509_crt_set_key_purpose_oid - This function sets the Certificate's key purpose OIDs + * @cert: should contain a gnutls_x509_crt_t structure + * @oid: a pointer to a null terminated string that holds the OID + * @critical: Whether this extension will be critical or not + * + * This function will set the key purpose OIDs of the Certificate. + * These are stored in the Extended Key Usage extension (2.5.29.37) + * See the GNUTLS_KP_* definitions for human readable names. + * + * Subsequent calls to this function will append OIDs to the OID list. + * + * On success 0 is returned. + * + **/ +int +gnutls_x509_crt_set_key_purpose_oid (gnutls_x509_crt_t cert, + const void *oid, unsigned int critical) +{ + int result; + gnutls_datum_t old_id, der_data; + ASN1_TYPE c2 = ASN1_TYPE_EMPTY; + + if (cert == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + result = asn1_create_element + (_gnutls_get_pkix (), "PKIX1.ExtKeyUsageSyntax", &c2); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + /* Check if the extension already exists. + */ + result = + _gnutls_x509_crt_get_extension (cert, "2.5.29.37", 0, &old_id, NULL); + + if (result >= 0) + { + /* decode it. + */ + result = asn1_der_decoding (&c2, old_id.data, old_id.size, NULL); + _gnutls_free_datum (&old_id); + + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + asn1_delete_structure (&c2); + return _gnutls_asn2err (result); + } + + } + + /* generate the extension. + */ + /* 1. create a new element. + */ + result = asn1_write_value (c2, "", "NEW", 1); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + asn1_delete_structure (&c2); + return _gnutls_asn2err (result); + } + + /* 2. Add the OID. + */ + result = asn1_write_value (c2, "?LAST", oid, 1); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + asn1_delete_structure (&c2); + return _gnutls_asn2err (result); + } + + result = _gnutls_x509_der_encode (c2, "", &der_data, 0); + asn1_delete_structure (&c2); + + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + result = _gnutls_x509_crt_set_extension (cert, "2.5.29.37", + &der_data, critical); + + _gnutls_free_datum (&der_data); + + if (result < 0) + { + gnutls_assert (); + return result; + } + + cert->use_extensions = 1; + + return 0; + +} + +#endif /* ENABLE_PKI */